Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

java.util.NoSuchElementException: No value present

Boa tarde pessoal da Alura!

Estou finalizando um projeto que contém autenticação via token, e toda vez que eu lanço um POST pra criação de um prato no meu programa ele retorna essa exception:

java.util.NoSuchElementException: No value present
    at java.base/java.util.Optional.get(Optional.java:148) ~[na:na]
    at br.com.develfoodspringweb.develfoodspringweb.security.AuthenticationTokenFilter.authenticateRestaurant(AuthenticationTokenFilter.java:83) ~[classes/:na]
    at br.com.develfoodspringweb.develfoodspringweb.security.AuthenticationTokenFilter.doFilterInternal(AuthenticationTokenFilter.java:56) ~[classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.10.jar:5.3.10]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.10.jar:5.3.10]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.2.jar:5.5.2]
    at ... // resto da exception
5 respostas

Essa exception é lançada toda vez que eu tento criar um prato de comida sem ter um restaurante com ID6 cadastrado no meu banco, se eu tiver um restaurate com id6 cadastrado no banco o prato é criado normalmente, entretanto se eu nao tiver esse restaurante retorna essa exception, segue classes para analise:

AuthenticationTokenFilter

package br.com.develfoodspringweb.develfoodspringweb.security;

import br.com.develfoodspringweb.develfoodspringweb.models.Restaurant;
import br.com.develfoodspringweb.develfoodspringweb.models.User;
import br.com.develfoodspringweb.develfoodspringweb.repository.RestaurantRepository;
import br.com.develfoodspringweb.develfoodspringweb.repository.UserRepository;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * Created by Luis Gregorio.
 *
 * class will intercept token request to grant user access.
 */
public class AuthenticationTokenFilter extends OncePerRequestFilter {

    private TokenServ tokenServs;
    private UserRepository userRepository;
    private RestaurantRepository restaurantRepository;

    public AuthenticationTokenFilter(TokenServ tokenServs, UserRepository userRepository, RestaurantRepository restaurantRepository) {
        this.tokenServs = tokenServs;
        this.userRepository = userRepository;
        this.restaurantRepository = restaurantRepository;
    }

    /**
     * Authentication by Token.
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     * @author: Luis Gregorio
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token = recoveryToken(request);
        boolean valid = tokenServs.isTokenValid(token);
        if(valid) {
            String userType = tokenServs.getUserType(token);
            if(userType.equals("user")) {
                authenticateCliente(token);
            } else if(userType.equals("restaurant")) {
                authenticateRestaurant(token);
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * Method to authenticate User
     * @param token
     * @author: Luis Gregorio
     */
    private void authenticateCliente(String token) {

        Long idUser = tokenServs.getIdUser(token);
        User users = userRepository.getById(idUser);
        System.out.println(users.getId());
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(users, null, users.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * Method to authenticate Restaurant
     * @param token
     * @author: Luis Gregorio
     */
    private void authenticateRestaurant(String token){
        Long idRestaurant = tokenServs.getIdRestaurant(token);
        Restaurant restaurant = restaurantRepository.findById(idRestaurant).get();
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(restaurant, null, restaurant.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);

    }

    /**
     * Method will cast a null if the token header does not meet any of these requirements.
     * @param request
     * @return
     * @author: Luis Gregorio
     */
    private String recoveryToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if(token == null || token.isEmpty() || !token.startsWith("Bearer ")) {
            return null;
        }
        return token.substring(7, token.length());
    }
}

Lembrando que se eu deixar um restaurante com ID6 cadastrado no meu data.sql, o problema não ocorre mais.

solução!

Oi Luís

Para evitar esse erro você pode verificar se o findById está retornando algo, ao invés de:

restaurantRepository.findById(idRestaurant).get()

algo como:

restaurantRepository.findById(idRestaurant).orElse(null)
restaurantRepository.findById(idRestaurant).orElseThrow(() -> new NotFoundException(""))

Bom dia Otávio!

Cara, eu atualizei pra essa linha aqui

Restaurant restaurant = restaurantRepository.findById(idRestaurant).orElse(null);

e como eu busco o id do restaurante para cadastrar um prato, eu tive que mudar o .get( ) tambem no meu service, que ficou assim:

public PlateDto register(PlateForm plateForm){
        Optional<Restaurant> restaurant = restaurantRepository.findById(plateForm.getRestaurantId());
        Plate plate = plateForm.convertToPlate(plateForm);
        plate.setRestaurant(restaurant.orElse(null));
        plateRepository.save(plate);
        return new PlateDto(plate);
    }

E agora funcionou, consigo criar um prato sem a necessidade de ter algum restaurante cadastrado no id 6.

Mas fiquei com uma dúvida, o que exatamente esse .orElse("null") faz no código?

Desde já agradeço o suporte maravilhoso de vocês! Grande abraço :D

Fala Luís, tudo certo?

Então, como o seu restaurant é um Optional<Restaurant> existem alguns métodos que você pode usar. O objetivo da classe Optional é fornecer uma solução em nível de tipo para representar valores opcionais em vez de referências nulas.

Por exemplo, para criar um objeto opcional vazio, simplesmente precisamos usar seu método estático empty():

Optional<String> empty = Optional.empty();
    assertFalse(empty.isPresent());

Por exemplo, com um tipo Optional podemos usar o método .isPresent() para verificar se existe algum valor, caso contrário o retorno desse método será false.

Já o método .orElse funciona assim, se existir algum valor no seu restaurant, esse valor é que será retornado, caso contrário será retornado o argumento que você passar para o .orElse, que no nosso caso foi null: restaurant.orElse(null)