Saltar al contenido

Cómo obtener el @RequestBody en un @ExceptionHandler (Spring REST)

Al fin luego de tanto batallar hemos dado con el resultado de esta dificultad que agunos usuarios de nuestro sitio han tenido. Si quieres aportar algún dato no dudes en dejar tu conocimiento.

Solución:

Puede hacer referencia al objeto del cuerpo de la solicitud a un bean con ámbito de solicitud. Y luego inyecte ese bean con ámbito de solicitud en su controlador de excepciones para recuperar el cuerpo de la solicitud (u otros beans de contexto de solicitud a los que desee hacer referencia).

// @Component
// @Scope("request")
@ManagedBean
@RequestScope
public class RequestContext 
    // fields, getters, and setters for request-scoped beans


@RestController
@RequestMapping("/api/v1/persons")
public class PersonController 

    @Inject
    private RequestContext requestContext;

    @Inject
    private PersonService personService;

    @PostMapping
    public Person savePerson(@RequestBody Person person) throws PersonServiceException 
         requestContext.setRequestBody(person);
         return personService.save(person);
    



@ControllerAdvice
public class ExceptionMapper 

    @Inject
    private RequestContext requestContext;

    @ExceptionHandler(PersonServiceException.class)
    protected ResponseEntity onPersonServiceException(PersonServiceException exception) 
         Object requestBody = requestContext.getRequestBody();
         // ...
         return responseEntity;
    

La respuesta aceptada crea un nuevo POJO para pasar las cosas, pero se puede lograr el mismo comportamiento sin crear objetos adicionales reutilizando la solicitud http.

Código de ejemplo para el mapeo del controlador:

public ResponseEntity savePerson(@RequestBody Person person, WebRequest webRequest) {
    webRequest.setAttribute("person", person, RequestAttributes.SCOPE_REQUEST);

Y más adelante, en la clase / método ExceptionHandler, puede usar:

@ExceptionHandler(Exception.class)
public ResponseEntity exceptionHandling(WebRequest request,Exception thrown) {

    Person person = (Person) request.getAttribute("person", RequestAttributes.SCOPE_REQUEST);

Debería poder obtener el contenido del cuerpo de la solicitud mediante la interfaz RequestBodyAdvice. Si implementa esto en una clase anotada con @ControllerAdvice, debería recogerse automáticamente.

Para obtener otra información de solicitud como el método HTTP y los parámetros de consulta, estoy usando un interceptor. Estoy capturando toda esta información de solicitud para el informe de errores en una variable ThreadLocal que borro en el gancho afterCompletion en ese mismo interceptor.

La siguiente clase implementa esto y se puede usar en su ExceptionHandler para obtener todos información requerida:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class RequestInfo extends HandlerInterceptorAdapter implements RequestBodyAdvice 
    private static final Logger logger = LoggerFactory.getLogger(RequestInfo.class);
    private static final ThreadLocal requestInfoThreadLocal = new ThreadLocal<>();

    private String method;
    private String body;
    private String queryString;
    private String ip;
    private String user;
    private String referrer;
    private String url;

    public static RequestInfo get() 
        RequestInfo requestInfo = requestInfoThreadLocal.get();
        if (requestInfo == null) 
            requestInfo = new RequestInfo();
            requestInfoThreadLocal.set(requestInfo);
        
        return requestInfo;
    

    public Map asMap() 
        Map map = new HashMap<>();
        map.put("method", this.method);
        map.put("url", this.url);
        map.put("queryParams", this.queryString);
        map.put("body", this.body);
        map.put("ip", this.ip);
        map.put("referrer", this.referrer);
        map.put("user", this.user);
        return map;
    

    private void setInfoFromRequest(HttpServletRequest request) 
        this.method = request.getMethod();
        this.queryString = request.getQueryString();
        this.ip = request.getRemoteAddr();
        this.referrer = request.getRemoteHost();
        this.url = request.getRequestURI();
        if (request.getUserPrincipal() != null) 
            this.user = request.getUserPrincipal().getName();
        
    

    public void setBody(String body) 
        this.body = body;
    

    private static void setInfoFrom(HttpServletRequest request) 
        RequestInfo requestInfo = requestInfoThreadLocal.get();
        if (requestInfo == null) 
            requestInfo = new RequestInfo();
        
        requestInfo.setInfoFromRequest(request);
        requestInfoThreadLocal.set(requestInfo);
    

    private static void clear() 
        requestInfoThreadLocal.remove();
    

    private static void setBodyInThreadLocal(String body) 
        RequestInfo requestInfo = get();
        requestInfo.setBody(body);
        setRequestInfo(requestInfo);
    

    private static void setRequestInfo(RequestInfo requestInfo) 
        requestInfoThreadLocal.set(requestInfo);
    

    // Implementation of HandlerInterceptorAdapter to capture the request info (except body) and be able to add it to the report in case of an error

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
        RequestInfo.setInfoFrom(request);
        return true;
    

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) 
        RequestInfo.clear();
    

    // Implementation of RequestBodyAdvice to capture the request body and be able to add it to the report in case of an error

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class> converterType) 
        return true;
    

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) 
        return inputMessage;
    

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) 
        RequestInfo.setBodyInThreadLocal(body.toString());
        return body;
    

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) 
        return body;
    

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *