¿Spring Boot personaliza la respuesta de error http?

¿Cómo puedo personalizar el código de estado de la respuesta y los datos en el cuerpo de la respuesta si ocurre una excepción en una aplicación web de Spring Boot?

Creé una aplicación web que lanza una excepción personalizada si ocurre algo inesperado debido a algún mal estado interno. En consecuencia, el cuerpo de la respuesta de la solicitud que desencadenó el error se parece a:

HTTP/1.1 500 Internal Server Error
{
    "timestamp": 1412685688268,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "com.example.CustomException",
    "message": null,
    "path": "/example"
}

Ahora, me gustaría cambiar el código de estado y establecer los campos en el cuerpo de la respuesta. Una solución que se me pasó por la cabeza fue algo como:

@ControllerAdvice
class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    ErrorMessage handleBadCredentials(CustomException e) {
        return new ErrorMessage("Bad things happened");
    }
}

@XmlRootElement
public class ErrorMessage(
    private String error;

    public ErrorMessage() {
    }

    public ErrorMessage(String error) {
        this.error = error;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
)

Sin embargo, eso creó (como se sospechaba) una respuesta completamente diferente:

HTTP/1.1 400 Bad Request
{
    "error": "Bad things happened"
}

preguntado Oct 07 '14, 14:10

@zeroflagL Preferiblemente, me gustaría personalizar la respuesta generada por Spring Boot (si es posible). La implementación de una solución personalizada completa (como la proporcionada en la pregunta) funciona, pero es menos reutilizable entre diferentes proyectos. -

Depende completamente de usted si la solución personalizada es reutilizable o no. FWIW: El cuerpo de respuesta está ensamblado por DefaultErrorAttributes#getErrorAttributes. Podrías inyectar esa clase en tu CustomResponseEntityExceptionHandler. -

@zeroflagL No pude hacer que su sugerencia funcionara (desplácese hacia abajo en el presenté). Sin embargo, obtuve ayuda para encontrar una solución, vea mi respuesta a continuación (o lea mi blog). -

2 Respuestas

Como mencionó @zeroflagL, Spring Boot fabrica el cuerpo de respuesta de error "estándar" en org.springframework.boot.autoconfigure.web.DefaultErrorAttributes. De manera similar a sus necesidades, quería aprovechar todo eso, pero simplemente aumentar un campo de "tipo" más que proporcionaron algunas de mis excepciones.

Lo hice implementando un Component que sub-clasificado DefaultErrorAttributes. Spring Boot lo recogió automáticamente y usó el mío en lugar del predeterminado.

@Component
public class ExtendedErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, 
                                                  boolean includeStackTrace) {
        final Map<String, Object> errorAttributes = 
            super.getErrorAttributes(requestAttributes, 
                                     includeStackTrace);

        final Throwable error = super.getError(requestAttributes);
        if (error instanceof TypeProvider) {
            final TypeProvider typeProvider = (TypeProvider) error;
            errorAttributes.put("type", typeProvider.getTypeIdentifier());
        }

        return errorAttributes;
    }
}

Con eso, obtengo un cuerpo de respuesta JSON aumentado, como

{
  "timestamp": 1488058582764,
  "status": 429,
  "error": "Too Many Requests",
  "exception": "com.example.ExternalRateLimitException",
  "message": "DAILY_LIMIT: too many requests",
  "path": "/api/lookup",
  "type": "DAILY_LIMIT"
}

Respondido 02 Abr '21, 08:04

¡Gran respuesta! También se puede modificar fácilmente para eliminar el atributo 'excepción' de la respuesta de error en las implementaciones de producción, si no está interesado en filtrar ese tipo de detalles. - Andrés

A partir de ahora (y probablemente desde Spring Boot 2) necesita extender org.springframework.boot.web.servlet.error.DefaultErrorAttributes que tiene org.springframework.web.context.request.WebRequest en los parámetros en lugar de RequestAttributes. - Sebastián van den Broek

Además, desde Spring Boot 2 getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) es obsoleto. usar getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions errorAttributeOptions) - Smile

El código de estado de respuesta http se puede cambiar usando el HttpServletResponse.sendError(int) método, por ejemplo

@ExceptionHandler
void handleIllegalArgumentException(IllegalArgumentException e, HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value());
}

Alternativamente, puede declarar el tipo de excepción en el @ExceptionHandler anotación si tiene dos o más excepciones para generar el mismo estado de respuesta:

@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
void handleBadRequests(HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value());
}

Puede encontrar más información en mi blog.

Respondido 19 Oct 14, 11:10

Vale la pena señalar que esto establece , solamente el código de estado de respuesta; el resto del mensaje de respuesta permanece igual. Si bien no es exactamente lo que preguntaba el autor de la pregunta, es exactamente lo que necesitaba para mi propio caso de uso. - M.Justin

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.