Search code examples
springspring-bootspring-securityspring-rest

Send Restful API JSON response from Spring Boot Filter


I am using following filter for checking if a specific HTTP Header is present on api request.

@Component
@RequiredArgsConstructor
public class HeaderValidationFilter extends OncePerRequestFilter {
private final ObjectMapper objectMapper;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        
String headerValue = request.getHeader("RANDOM_HEADER");

if(Objects.isNull(headerValue)) {
    ResponseEntity<Object> responseToSend = ResponseGen.create("FAILED", "Missing Authentication Header", new Object())));

    response.setHeader("Content-Type", "application/json");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.getOutputStream().write(responseToSend);
    return;
}

    filterChain.doFilter(request, response);
}

It returns response as JSON but with added object keys like headers,statusCodeetc and the payload I provided is placed inside body. As I have a standard error response defined on the Project, I cannot return the response at it is. (See the following response)

{
"headers": {},
"body": {
    "status": "FAILURE",
    "message": "Missing Authentication Header",
    "data": {}
},
"statusCode": "UNAUTHORIZED",
"statusCodeValue": 401
}

I want the response to be only in this format:

{
    "status": "FAILED",
    "message": "Missing Authentication Header",
    "data": {}
}

I have tried returning using generic exception handler with @ControllerAdvice but it doesn't capture exception from Filters as it is executed before DispatcherServlet

Can someone help me out?


Solution

  • The JSON you see

    {
    "headers": {...},
    "body": {...},
    "statusCode": ...,
    "statusCodeValue": ...
    }
    

    is the serialized ResponseEntity that you write as a response in your filter:

    ResponseEntity<Object> responseToSend = ResponseGen.create("FAILED", "Missing Authentication Header", new Object())));
    

    Obviously, if you need a response in another format you'll have to use an object that is serialized to the desired format as a response. E.g.

    @Data
    @AllArgsConstructor
    class StandardError {
        private String status;
        private String message;
        private Object data;
    }
    
    ....
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        ...
        if (Objects.isNull(headerValue)) {
            StandardError responseToSend = new StandardError("FAILED", "Missing Authentication Header", new Object());
    
            response.setHeader("Content-Type", "application/json");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getOutputStream().write(responseToSend);
            return;
        }
        ...