Gatilho 404 no controlador Spring-MVC?

193

Como obtenho um controlador Spring 3.0 para disparar um 404?

Eu tenho um controlador com @RequestMapping(value = "/**", method = RequestMethod.GET)e para alguns URLs acessando o controlador, quero que o contêiner crie um 404.

N / D.
fonte

Respostas:

326

Desde o Spring 3.0, você também pode lançar uma exceção declarada com @ResponseStatusanotação:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    ...
}

@Controller
public class SomeController {
    @RequestMapping.....
    public void handleCall() {
        if (isFound()) {
            // whatever
        }
        else {
            throw new ResourceNotFoundException(); 
        }
    }
}
axtavt
fonte
2
Interessante. Você pode especificar qual HttpStatus usar no site de lançamento (ou seja, não o compilar na classe Exception)?
Matt b
1
@mattb: Eu acho que o objetivo @ResponseStatusé que você defina um monte de classes de exceção bem tipificadas e fortemente tipadas, cada uma com a sua @ResponseStatus. Dessa forma, você separa o código do controlador dos detalhes dos códigos de status HTTP.
skaffman
9
Isso pode ser estendido para dar suporte ao retorno de um corpo que contém mais descrição sobre o erro?
Tom
7
@Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun
6
Se você usar esta exceção ResourceNotFound apenas para controle de fluxo, talvez seja uma boa idéia substituir ResourceNotFound.fillInStackTrace()por uma implementação vazia.
Ralph
36

Reescreva sua assinatura de método para que ela aceite HttpServletResponsecomo parâmetro, para que você possa acessá setStatus(int)-la.

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments

matt b
fonte
8
Essa é a única resposta correta se alguém estiver procurando uma maneira de informar ao solicitante http que cometeu um erro, enquanto não inundava a equipe de produtos com várias exceções que não podem ser corrigidas.
Alex R
4
setStatus(int)javadoc declara o seguinte: Se este método for usado para definir um código de erro, o mecanismo da página de erro do contêiner não será acionado. Se houver um erro e o chamador desejar chamar uma página de erro definida no aplicativo da web, ele sendErrordeverá ser usado.
Philippe Gioseffi
As exceções tratadas pelo @AlexR não devem inundar a equipe de operações. Se estiverem, o log está sendo feito incorretamente.
Raedwald
25

Gostaria de mencionar que há uma exceção (não apenas) para 404, por padrão, fornecida pelo Spring. Consulte a documentação do Spring para obter detalhes. Portanto, se você não precisar de sua própria exceção, basta fazer o seguinte:

 @RequestMapping(value = "/**", method = RequestMethod.GET)
 public ModelAndView show() throws NoSuchRequestHandlingMethodException {
    if(something == null)
         throw new NoSuchRequestHandlingMethodException("show", YourClass.class);

    ...

  }
michal.kreuzman
fonte
11
Isso parece destinado a um caso específico - quando o Spring não consegue encontrar um manipulador. O caso em questão é quando o Spring pode encontrar um manipulador, mas o usuário deseja retornar um 404 por outro motivo.
Roy Truelove
2
Estou usando quando meu mapeamento de ulr para o método manipulador é dinâmico. Quando a entidade não existe com base em, @PathVariablenão há tratamento de solicitações do meu ponto de vista. Você acha que é melhor / mais limpo usar sua própria exceção anotada @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman
1
No seu caso, parece bom, mas não sei se recomendo as exceções encontradas no link que você forneceu para lidar com todos os casos em que uma exceção é necessária - às vezes você deve criar as suas próprias.
Roy Truelove
Bem, o Spring forneceu uma exceção e uma apenas para o 404. Eles deveriam ter chamado 404Exception ou criado um. Mas como é agora, eu acho que é ok para jogar isso sempre que você precisa de um 404.
autra
Bem, tecnicamente falando, tudo bem - você enviará o cabeçalho do status 404. Mas a mensagem de erro automática - conteúdo da resposta - é "Nenhum método de manipulação de solicitação com nome ...", que provavelmente não é algo que você deseja mostrar ao usuário.
Olli
24

Desde o Spring 3.0.2, você pode retornar o ResponseEntity <T> como resultado do método do controlador:

@RequestMapping.....
public ResponseEntity<Object> handleCall() {
    if (isFound()) {
        // do what you want
        return new ResponseEntity<>(HttpStatus.OK);
    }
    else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

(ResponseEntity <T> é mais flexível que a anotação @ResponseBody - veja outra pergunta )

Lu55
fonte
2
flexível de curso, mas derrota os benefícios da programação declarativa
rohanagarwal
3
Se você estiver usando o Sentry ou semelhante no PROD, e não quiser enviá-lo por spam com erros que não são reais, essa solução é muito melhor em comparação com a que usa exceções para essa situação não excepcional.
Tobias Hermann
Não se esqueça de como preencher o corpo (com seu objeto real). Exemplo genérico de "Objeto": Objeto returnItemBody = new Object (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder
16

você pode usar o @ControllerAdvice para lidar com suas exceções. O comportamento padrão da classe anotada @ControllerAdvice ajudará todos os controladores conhecidos.

por isso, será chamado quando qualquer controlador que você lança erro 404.

como o seguinte:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.NOT_FOUND)  // 404
    @ExceptionHandler(Exception.class)
    public void handleNoTFound() {
        // Nothing to do
    }
}

e mapeie esse erro de resposta 404 no seu web.xml, como o seguinte:

<error-page>
        <error-code>404</error-code>
        <location>/Error404.html</location>
</error-page>

Espero que ajude .


fonte
2
você mapeou exceções do tipo Exceção (e subclasses) com um código de status 404. Você já pensou que existem erros internos no servidor? Como você planeja lidar com aqueles em seu GlobalControllerExceptionHandler?
rohanagarwal
Isso NÃO funcionou para controladores REST, retorna uma resposta vazia.
rustyx 11/09
10

Se o método do seu controlador for para algo como manipulação de arquivos, ResponseEntityé muito útil:

@Controller
public class SomeController {
    @RequestMapping.....
    public ResponseEntity handleCall() {
        if (isFound()) {
            return new ResponseEntity(...);
        }
        else {
            return new ResponseEntity(404);
        }
    }
}
Ralph
fonte
9

Embora a resposta marcada esteja correta, há uma maneira de conseguir isso sem exceções. O serviço está retornando Optional<T>do objeto pesquisado e isso é mapeado para HttpStatus.OKse encontrado e para 404 se estiver vazio.

@Controller
public class SomeController {

    @RequestMapping.....
    public ResponseEntity<Object> handleCall() {
        return  service.find(param).map(result -> new ResponseEntity<>(result, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

@Service
public class Service{

    public Optional<Object> find(String param){
        if(!found()){
            return Optional.empty();
        }
        ...
        return Optional.of(data); 
    }

}
user1121883
fonte
Eu gosto dessa abordagem em geral, mas o uso de Optionals às vezes acaba sendo um antipadrão. E fica complicado ao retornar coleções.
Jfzr 28/08/19
7

Eu recomendo lançar HttpClientErrorException , como este

@RequestMapping(value = "/sample/")
public void sample() {
    if (somethingIsWrong()) {
        throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    }
}

Você deve se lembrar que isso só pode ser feito antes que algo seja gravado no fluxo de saída do servlet.

mmatczuk
fonte
4
Essa exceção é lançada pelo cliente HTTP Spring. O Spring MVC parece não reconhecer essa exceção. Qual versão do Spring você está usando? Você está recebendo um 404 com essa exceção?
Eduardo
1
Isso faz com que o Spring Boot retorne:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
slim
Esta é uma exceção para um cliente HTTP, não para um controlador. Portanto, usá-lo no contexto especificado é inadequado.
Alexey
3

É um pouco tarde, mas se você estiver usando o Spring Data REST , já existe. org.springframework.data.rest.webmvc.ResourceNotFoundException Ele também usa @ResponseStatusanotação. Não há mais necessidade de criar uma exceção de tempo de execução customizada.

piloto
fonte
2

Além disso, se você deseja retornar o status 404 do seu controlador, tudo o que você precisa é fazer isso

@RequestMapping(value = "/somthing", method = RequestMethod.POST)
@ResponseBody
public HttpStatus doSomthing(@RequestBody String employeeId) {
    try{
  return HttpStatus.OK;
    } 
    catch(Exception ex){ 
  return HttpStatus.NOT_FOUND;
    }
}

Ao fazer isso, você receberá um erro 404 no caso em que deseja retornar um 404 do seu controlador.

AbdusSalam
fonte
0

Simplesmente você pode usar o web.xml para adicionar código de erro e página de erro 404. Mas verifique se a página de erro 404 não deve ser localizada em WEB-INF.

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>

Essa é a maneira mais simples de fazer isso, mas isso tem algumas limitações. Suponha que, se você deseja adicionar o mesmo estilo para esta página, você adicionou outras páginas. Desta forma, você não pode fazer isso. Você tem que usar o@ResponseStatus(value = HttpStatus.NOT_FOUND)

Rajith Delantha
fonte
Essa é a maneira de fazer isso, mas considere com isso a HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;partir do código do controlador. Agora, do lado de fora, a resposta não parece diferente de um 404 normal que não atingiu nenhum controlador.
Darryl Miles
isso não aciona um 404, ele apenas lida com isso se acontecer um
Alex R
0

Configure web.xml com a configuração

<error-page>
    <error-code>500</error-code>
    <location>/error/500</location>
</error-page>

<error-page>
    <error-code>404</error-code>
    <location>/error/404</location>
</error-page>

Criar novo controlador

   /**
     * Error Controller. handles the calls for 404, 500 and 401 HTTP Status codes.
     */
    @Controller
    @RequestMapping(value = ErrorController.ERROR_URL, produces = MediaType.APPLICATION_XHTML_XML_VALUE)
    public class ErrorController {


        /**
         * The constant ERROR_URL.
         */
        public static final String ERROR_URL = "/error";


        /**
         * The constant TILE_ERROR.
         */
        public static final String TILE_ERROR = "error.page";


        /**
         * Page Not Found.
         *
         * @return Home Page
         */
        @RequestMapping(value = "/404", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView notFound() {

            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current.");

            return model;
        }

        /**
         * Error page.
         *
         * @return the model and view
         */
        @RequestMapping(value = "/500", produces = MediaType.APPLICATION_XHTML_XML_VALUE)
        public ModelAndView errorPage() {
            ModelAndView model = new ModelAndView(TILE_ERROR);
            model.addObject("message", "The page you requested could not be found. This location may not be current, due to the recent site redesign.");

            return model;
        }
}
Atish Narlawar
fonte
0

Porque é sempre bom ter pelo menos dez maneiras de fazer a mesma coisa:

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Something {
    @RequestMapping("/path")
    public ModelAndView somethingPath() {
        return new ModelAndView("/", HttpStatus.NOT_FOUND);
    }
}
Jason
fonte