¿Disparador 404 en el controlador Spring-MVC?

193

¿Cómo consigo un controlador Spring 3.0 para activar un 404?

Tengo un controlador con @RequestMapping(value = "/**", method = RequestMethod.GET)y para algunas URL que acceden al controlador, quiero que el contenedor tenga un 404.

N / A.
fuente

Respuestas:

326

Desde Spring 3.0 también puede lanzar una excepción declarada con @ResponseStatusanotación:

@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
fuente
2
Interesante. ¿Puede especificar qué HttpStatus usar en el sitio de lanzamiento (es decir, no tenerlo compilado en la clase Excepción)?
mate b
1
@mattb: Creo que el punto @ResponseStatuses que usted define un montón de clases de excepciones bien tipadas y bien nombradas, cada una con la suya propia @ResponseStatus. De esa manera, desacoplas el código de tu controlador del detalle de los códigos de estado HTTP.
skaffman
9
¿Se puede ampliar para admitir la devolución de un cuerpo que contiene más descripción sobre el error?
Tom
77
@Tom:@ResponseStatus(value = HttpStatus.NOT_FOUND, reason="Your reason")
Nailgun
66
Si usa esta excepción ResourceNotFound solo para el control de flujo, entonces tal vez sea una buena idea anular ResourceNotFound.fillInStackTrace()con una implementación vacía.
Ralph
36

Vuelva a escribir su firma de método para que acepte HttpServletResponsecomo parámetro, de modo que pueda invocarlo setStatus(int).

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

mate b
fuente
8
Esta es la única respuesta correcta si alguien está buscando una manera de decirle al solicitante de http que cometió un error sin inundar al equipo de operaciones con una serie de excepciones que no pueden solucionar.
Alex R
44
setStatus(int)javadoc dice lo siguiente: si este método se usa para establecer un código de error, entonces el mecanismo de la página de error del contenedor no se activará. Si hay un error y la persona que llama desea invocar una página de error definida en la aplicación web, sendErrordebe usarse en su lugar.
Philippe Gioseffi
Las excepciones manejadas por @AlexR no deberían estar inundando el equipo de operaciones. Si lo están, el registro se está haciendo incorrectamente.
Raedwald
25

Me gustaría mencionar que hay una excepción (no solo) para 404 por defecto proporcionada por Spring. Consulte la documentación de Spring para más detalles. Entonces, si no necesita su propia excepción, simplemente puede hacer esto:

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

    ...

  }
michal.kreuzman
fuente
11
Esto parece estar destinado a un caso específico, cuando Spring no puede encontrar un controlador. El caso en cuestión es cuando Spring puede encontrar un controlador, pero el usuario desea devolver un 404 por otro motivo.
Roy Truelove
2
Lo estoy usando cuando mi mapeo ulr para el método del controlador es dinámico. Cuando la entidad no existe en función @PathVariablede mi punto de vista, no hay manejo de solicitudes. ¿Crees que es mejor / más limpio usar tu propia Excepción anotada @ResponseStatus(value = HttpStatus.NOT_FOUND) ?
michal.kreuzman
1
En su caso, suena bien, pero no sé si recomendaría las excepciones que se encuentran en el enlace que proporcionó para manejar todos los casos en los que sea necesaria una excepción, a veces debe hacer la suya propia.
Roy Truelove
Bueno, Spring proporcionó una excepción y una solo para 404. Deberían haberla llamado 404Exception o haber creado una. Pero como está ahora, creo que está bien tirar esto siempre que necesite un 404.
autra
Bueno, técnicamente hablando está bien: enviarás un encabezado de estado 404. Pero el mensaje de error automático - contenido de respuesta - es "No hay método de manejo de solicitudes con nombre ...", que probablemente no sea algo que desee mostrar al usuario.
Olli
24

Desde Spring 3.0.2 puede devolver ResponseEntity <T> como resultado del método del 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> es una anotación más flexible que @ResponseBody - vea otra pregunta )

Lu55
fuente
2
flexible, por supuesto, pero derrota los beneficios de la programación declarativa
rohanagarwal
3
Si está utilizando Sentry o similar en PROD, y no desea enviarlo por correo no deseado con errores que no son errores reales, esta solución es mucho mejor en comparación con la que usa excepciones para esta situación no excepcional.
Tobias Hermann
No olvides cómo llenar el cuerpo (con tu objeto real). Ejemplo genérico de "Objeto": Object returnItemBody = new Object (); return ResponseEntity.status (HttpStatus.OK) .body (returnItemBody);
granadaCoder
16

puede usar el @ControllerAdvice para manejar sus Excepciones. El comportamiento predeterminado de la clase anotada @ControllerAdvice ayudará a todos los Controladores conocidos.

por lo que se llamará cuando cualquier controlador que tenga arroje un error 404.

como el siguiente:

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

y mapee este error de respuesta 404 en su web.xml, como el siguiente:

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

Espero que ayude .


fuente
2
ha asignado excepciones de tipo Excepción (y subclases) con un código de estado 404. ¿Alguna vez pensaste que hay errores internos del servidor? ¿Cómo planea manejarlos en su GlobalControllerExceptionHandler?
rohanagarwal
Esto NO funcionó para los controladores REST, devuelve una respuesta vacía.
rustyx
10

Si su método de controlador es para algo como el manejo de archivos, entonces ResponseEntityes muy útil:

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

Si bien la respuesta marcada es correcta, hay una manera de lograr esto sin excepciones. El servicio devuelve Optional<T>el objeto buscado y esto se asigna a HttpStatus.OKsi se encuentra y a 404 si está vacío.

@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); 
    }

}
usuario1121883
fuente
Me gusta este enfoque en general, pero el uso de Opcionales a veces termina siendo un antipatrón. Y se complica al devolver las colecciones.
jfzr
7

Recomiendo lanzar HttpClientErrorException , como este

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

Debe recordar que esto solo se puede hacer antes de escribir algo en la secuencia de salida del servlet.

mmatczuk
fuente
44
El cliente Spring Spring lanza esa excepción. Spring MVC parece no reconocer esa excepción. ¿Qué versión de Spring estás usando? ¿Estás obteniendo un 404 con esa excepción?
Eduardo
1
Esto hace que Spring Boot regrese:Whitelabel Error Page \n .... \n There was an unexpected error (type=Internal Server Error, status=500). \n 404 This is your not found error
delgado
Esta es una excepción para un cliente HTTP, no para un controlador. Por lo tanto, usarlo en el contexto especificado es inapropiado.
Alexey
3

Esto es un poco tarde, pero si está utilizando Spring Data REST, entonces ya existe. org.springframework.data.rest.webmvc.ResourceNotFoundException También utiliza @ResponseStatusanotaciones. Ya no es necesario crear una excepción de tiempo de ejecución personalizada.

piloto
fuente
2

Además, si desea devolver el estado 404 de su controlador, todo lo que necesita es hacer esto

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

Al hacer esto, recibirá un error 404 en caso de que desee devolver un 404 desde su controlador.

AbdusSalam
fuente
0

Simplemente puede usar web.xml para agregar código de error y página de error 404. Pero asegúrese de que la página de error 404 no debe ubicarse en WEB-INF.

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

Esta es la forma más sencilla de hacerlo, pero tiene algunas limitaciones. Suponga que si desea agregar el mismo estilo para esta página que agregó otras páginas. De esta manera no puedes hacerlo. Tienes que usar el@ResponseStatus(value = HttpStatus.NOT_FOUND)

Rajith Delantha
fuente
Esta es la forma de hacerlo, pero considérelo con HttpServletResponse#sendError(HttpServletResponse.SC_NOT_FOUND); return null;el código del controlador. Ahora, desde el exterior, la respuesta no se ve diferente a un 404 normal que no golpeó ningún controlador.
Darryl Miles
esto no desencadena un 404, solo lo maneja si ocurre uno
Alex R
0

Configure web.xml con la configuración

<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>

Crear nuevo 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
fuente
0

Porque siempre es bueno tener al menos diez formas de hacer lo mismo:

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
fuente