Cómo responder con un error HTTP 400 en un método Spring MVC @ResponseBody que devuelve String
⏰ 8 minutos de lectura
Jonik
Estoy usando Spring MVC para una API JSON simple, con @ResponseBody enfoque basado como el siguiente. (Ya tengo una capa de servicio que produce JSON directamente).
@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
String json = matchService.getMatchJson(matchId);
if (json == null) {
// TODO: how to respond with e.g. 400 "bad request"?
}
return json;
}
En el escenario dado, ¿cuál es la forma más simple y limpia de responder con un error HTTP 400?
Me encontré con enfoques como:
return new ResponseEntity(HttpStatus.BAD_REQUEST);
… pero no puedo usarlo aquí ya que el tipo de retorno de mi método es String, no ResponseEntity.
Bassem Reda Zohdy
Cambia tu tipo de devolución a ResponseEntity<>y luego puede usar lo siguiente para 400:
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
Y para una solicitud correcta:
return new ResponseEntity<>(json,HttpStatus.OK);
Después de Spring 4.1, hay métodos auxiliares en ResponseEntity que podrían usarse como:
¡Gracias! Esto funciona y es bastante simple también. (En este caso, se podría simplificar aún más eliminando los elementos no utilizados) body y request parámetros.)
– Jonik
27 de abril de 2013 a las 9:18
Zutty
No es necesariamente la forma más compacta de hacer esto, pero en mi opinión es bastante limpia:
if(json == null) {
throw new BadThingException();
}
...
@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
return new MyError("That doesn’t work");
}
Puede usar @ResponseBody en el método del controlador de excepciones si usa Spring 3.1+; de lo contrario, use un ModelAndView o algo.
Lo sentimos, esto no parece funcionar. Produce un “error de servidor” HTTP 500 con un seguimiento de pila largo en los registros: ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: public controller.TestController$MyError controller.TestController.handleException(controller.TestController$BadThingException) org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation ¿Falta algo en la respuesta?
– Jonik
27 de abril de 2013 a las 8:52
Además, no entendí completamente el punto de definir otro tipo personalizado (MyError). ¿Es eso necesario? Estoy usando la última primavera (3.2.2).
– Jonik
27 de abril de 2013 a las 8:54
Esto funciona para mi. yo suelo javax.validation.ValidationException en cambio. (Primavera 3.1.4)
–Jerry Chen
1 de octubre de 2013 a las 1:48
Esto es bastante útil en situaciones en las que tiene una capa intermedia entre su servicio y el cliente donde la capa intermedia tiene sus propias capacidades de manejo de errores. Gracias por este ejemplo @Zutty
– StormeHawke
9 dic 2014 a las 15:57
Esta debería ser la respuesta aceptada, ya que mueve el código de manejo de excepciones fuera del flujo normal y oculta HttpServlet*
– lilalinux
14 de septiembre de 2016 a las 12:17
Matsev
Cambiaría un poco la implementación:
Primero creo un UnknownMatchException:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
public UnknownMatchException(String matchId) {
super("Unknown match: " + matchId);
}
}
Nótese el uso de @ResponseStatusque será reconocida por Spring’s ResponseStatusExceptionResolver. Si se lanza la excepción, creará una respuesta con el estado de respuesta correspondiente. (También me tomé la libertad de cambiar el código de estado a 404 - Not Found que me parece más apropiado para este caso de uso, pero puede ceñirse a HttpStatus.BAD_REQUEST Si te gusta.)
A continuación, cambiaría el MatchService tener la siguiente firma:
interface MatchService {
public Match findMatch(String matchId);
}
Finalmente, actualizaría el controlador y delegaría a Spring’s MappingJackson2HttpMessageConverter para manejar la serialización JSON automáticamente (se agrega de forma predeterminada si agrega Jackson al classpath y agrega cualquiera @EnableWebMvc o <mvc:annotation-driven /> a su configuración. Ver el documentación de referencia):
@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Match match(@PathVariable String matchId) {
// Throws an UnknownMatchException if the matchId is not known
return matchService.findMatch(matchId);
}
Tenga en cuenta que es muy común separar los objetos de dominio de los objetos de vista o DTO objetos. Esto se puede lograr fácilmente agregando una pequeña fábrica de DTO que devuelve el objeto JSON serializable:
@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MatchDTO match(@PathVariable String matchId) {
Match match = matchService.findMatch(matchId);
return MatchDtoFactory.createDTO(match);
}
Aquí hay un enfoque diferente. crear una costumbre Exception anotado con @ResponseStatuscomo el siguiente.
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception {
public NotFoundException() {
}
}
Y tirarlo cuando sea necesario.
@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
String json = matchService.getMatchJson(matchId);
if (json == null) {
throw new NotFoundException();
}
return json;
}
Este enfoque le permite finalizar la ejecución dondequiera que se encuentre en el seguimiento de la pila sin tener que devolver un “valor especial” que debe especificar el código de estado HTTP que desea devolver.
– Muhammad Gelbana
28 de noviembre de 2017 a las 5:34
El enlace está (efectivamente) roto. No hay nada sobre excepciones en él.
-Peter Mortensen
19 de junio a las 12:26
Pedro Mortensen
La forma más fácil es lanzar un ResponseStatusException:
Este enfoque le permite finalizar la ejecución dondequiera que se encuentre en el seguimiento de la pila sin tener que devolver un “valor especial” que debe especificar el código de estado HTTP que desea devolver.
– Muhammad Gelbana
28 de noviembre de 2017 a las 5:34
El enlace está (efectivamente) roto. No hay nada sobre excepciones en él.
-Peter Mortensen
19 de junio a las 12:26
norris
Como se menciona en algunas respuestas, existe la posibilidad de crear una clase de excepción para cada estado HTTP que desee devolver. No me gusta la idea de tener que crear una clase por estado para cada proyecto. Esto es lo que se me ocurrió en su lugar.
Cree una excepción genérica que acepte un estado HTTP
Crear un controlador de excepciones de consejos de controlador
Vayamos al código
package com.javaninja.cam.exception;
import org.springframework.http.HttpStatus;
/**
* The exception used to return a status and a message to the calling system.
* @author norrisshelton
*/
@SuppressWarnings("ClassWithoutNoArgConstructor")
public class ResourceException extends RuntimeException {
private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
/**
* Gets the HTTP status code to be returned to the calling system.
* @return http status code. Defaults to HttpStatus.INTERNAL_SERVER_ERROR (500).
* @see HttpStatus
*/
public HttpStatus getHttpStatus() {
return httpStatus;
}
/**
* Constructs a new runtime exception with the specified HttpStatus code and detail message.
* The cause is not initialized, and may subsequently be initialized by a call to {@link #initCause}.
* @param httpStatus the http status. The detail message is saved for later retrieval by the {@link
* #getHttpStatus()} method.
* @param message the detail message. The detail message is saved for later retrieval by the {@link
* #getMessage()} method.
* @see HttpStatus
*/
public ResourceException(HttpStatus httpStatus, String message) {
super(message);
this.httpStatus = httpStatus;
}
}
Luego creo una clase de consejos de controlador.
package com.javaninja.cam.spring;
import com.javaninja.cam.exception.ResourceException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* Exception handler advice class for all SpringMVC controllers.
* @author norrisshelton
* @see org.springframework.web.bind.annotation.ControllerAdvice
*/
@org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {
/**
* Handles ResourceExceptions for the SpringMVC controllers.
* @param e SpringMVC controller exception.
* @return http response entity
* @see ExceptionHandler
*/
@ExceptionHandler(ResourceException.class)
public ResponseEntity handleException(ResourceException e) {
return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
}
}
para usarlo
throw new ResourceException(HttpStatus.BAD_REQUEST, "My message");
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información