example - springboot error handling
Cuerpo de excepción vacío en Spring MVC Test (2)
Esto probablemente significa que no manejó la excepción o que realmente dejó el cuerpo vacío. Para manejar la excepción, agregue un controlador de errores en el controlador
@ExceptionHandler
public @ResponseBody String handle(BadRequestException e) {
return "I''m the body";
}
o usuario, el controlador de error global si tiene 3.2 o superior
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public @ResponseBody String handleBadRequestException(BadRequestException ex) {
return "I''m the body";
}
}
con esto, el cuerpo se poblará, debes rellenarlo con tu mensaje de error
Estoy teniendo problemas al intentar que MockMvc incluya el mensaje de excepción en el cuerpo de la respuesta. Tengo un controlador de la siguiente manera:
@RequestMapping("/user/new")
public AbstractResponse create(@Valid NewUserParameters params, BindingResult bindingResult) {
if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
// ...
}
donde BadRequestException
ve así:
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
public class BadRequestException extends IllegalArgumentException {
public BadRequestException(String cause) { super(cause); }
public static BadRequestException of(BindingResult bindingResult) { /* ... */ }
}
Y ejecuto la siguiente prueba contra /user/new
controller:
@Test
public void testUserNew() throws Exception {
getMockMvc().perform(post("/user/new")
.param("username", username)
.param("password", password))
.andDo(print())
.andExpect(status().isOk());
}
que imprime el siguiente resultado:
Resolved Exception:
Type = controller.exception.BadRequestException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = bad request
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
¿Alguien tiene una idea de por qué falta Body
en la salida print()
?
Editar: no estoy usando ningún manejador de excepción personalizado y el código funciona como se espera cuando ejecuto el servidor. Es decir, ejecutar la aplicación y hacer que la misma solicitud al servidor regrese
{"timestamp":1423076185822,
"status":400,
"error":"Bad Request",
"exception":"controller.exception.BadRequestException",
"message":"binding failed for field(s): password, username, username",
"path":"/user/new"}
como se esperaba. Por lo tanto, supongo que hay un problema con el MockMvc
. De alguna manera no capta el campo de message
de la excepción, mientras que el controlador de excepción predeterminado del servidor de aplicaciones normal funciona como se esperaba.
Después de abrir un ticket para el problema, me informaron que el mensaje de error en el cuerpo está a cargo de Spring Boot, que configura las asignaciones de error en el nivel del contenedor de Servlet y, dado que Spring MVC Test se ejecuta con una solicitud / respuesta de Servlet, hay sin tal mapeo de error. Además, me recomendaron crear al menos un @WebIntegrationTest
y @WebIntegrationTest
a Spring MVC Test para la lógica de mi controlador.
Eventualmente, decidí ir con mi propio manejador de excepciones personalizado y MockMvc
a MockMvc
para el resto como antes.
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(Throwable.class)
public @ResponseBody
ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
HttpStatus status = Optional
.ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
.map(ResponseStatus::value)
.orElse(HttpStatus.INTERNAL_SERVER_ERROR);
response.setStatus(status.value());
return new ExceptionResponse(throwable.getMessage());
}
}
@Data
public class ExceptionResponse extends AbstractResponse {
private final long timestamp = System.currentTimeMillis();
private final String message;
@JsonCreator
public ExceptionResponse(String message) {
checkNotNull(message, "message == NULL");
this.message = message;
}
}