Spring REST
Exception Handling
@RequestBody
@ExceptionHandler
Java

How to get the RequestBody in an ExceptionHandler Spring REST

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Reading the request body inside a Spring @ExceptionHandler is not straightforward because the HTTP input stream is normally consumed during request processing. If you want the raw body for logging or diagnostics after an exception, you need to cache it earlier in the request lifecycle.

Why the Body Is Not Directly Available

By the time an exception reaches @ExceptionHandler, Spring may already have read the request stream to deserialize @RequestBody into a Java object. A servlet request body is not freely reusable by default, so trying to read it again often returns nothing.

That is why the usual solution is not "read it later," but "wrap and cache it before the controller consumes it."

Use ContentCachingRequestWrapper

Spring provides ContentCachingRequestWrapper, which stores request content as it is read. A filter can wrap every incoming request so the cached bytes remain available during exception handling.

java
1import jakarta.servlet.FilterChain;
2import jakarta.servlet.ServletException;
3import jakarta.servlet.http.HttpServletRequest;
4import jakarta.servlet.http.HttpServletResponse;
5import org.springframework.stereotype.Component;
6import org.springframework.web.filter.OncePerRequestFilter;
7import org.springframework.web.util.ContentCachingRequestWrapper;
8
9import java.io.IOException;
10
11@Component
12public class RequestBodyCachingFilter extends OncePerRequestFilter {
13
14    @Override
15    protected void doFilterInternal(
16            HttpServletRequest request,
17            HttpServletResponse response,
18            FilterChain filterChain) throws ServletException, IOException {
19
20        ContentCachingRequestWrapper wrappedRequest =
21                new ContentCachingRequestWrapper(request);
22
23        filterChain.doFilter(wrappedRequest, response);
24    }
25}

This wrapper does not magically read the body up front. It caches content as other parts of the stack read it. That is usually enough for controller exceptions, validation failures, and message conversion errors.

Access the Cached Body in @ControllerAdvice

Once the request is wrapped, an exception handler can inspect the cached bytes by looking at the current HttpServletRequest.

java
1import jakarta.servlet.http.HttpServletRequest;
2import org.springframework.http.HttpStatus;
3import org.springframework.http.ResponseEntity;
4import org.springframework.web.bind.annotation.ControllerAdvice;
5import org.springframework.web.bind.annotation.ExceptionHandler;
6import org.springframework.web.util.ContentCachingRequestWrapper;
7
8import java.nio.charset.StandardCharsets;
9
10@ControllerAdvice
11public class GlobalExceptionHandler {
12
13    @ExceptionHandler(Exception.class)
14    public ResponseEntity<String> handleException(Exception ex, HttpServletRequest request) {
15        String body = "";
16
17        if (request instanceof ContentCachingRequestWrapper wrapper) {
18            byte[] content = wrapper.getContentAsByteArray();
19            body = new String(content, StandardCharsets.UTF_8);
20        }
21
22        System.err.println("Request body: " + body);
23        System.err.println("Error: " + ex.getMessage());
24
25        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
26                .body("Request failed");
27    }
28}

If the body was consumed during request handling, getContentAsByteArray() returns the cached content. That gives you safe access without trying to reopen the input stream.

When You Need the Deserialized Object Instead

If your real goal is not the raw JSON string but the parsed request payload, another option is to log or store the deserialized object before risky business logic runs. That can be cleaner than working with raw bytes in a global handler.

For example, a controller can validate or log the request object immediately after binding and then call service code that may throw later. That approach avoids double work and usually produces more structured logs.

Common Pitfalls

The most common mistake is trying to call request.getInputStream() directly inside the exception handler. In many cases the stream has already been consumed, so the result is empty or triggers another error.

Another pitfall is assuming ContentCachingRequestWrapper populates itself before anything reads the body. It caches what passes through it. If nothing ever reads the body, there may be nothing cached.

Character encoding also matters. If you always decode bytes as UTF-8 but a request used a different charset, the logged body may be garbled. In real systems, prefer request.getCharacterEncoding() when available.

Finally, be careful with sensitive data. Logging the full request body can expose passwords, tokens, or personal information. In production systems, it is often better to redact specific fields or limit body logging to known-safe endpoints.

Summary

  • A Spring exception handler cannot usually reread the raw request stream directly.
  • Wrap requests early with ContentCachingRequestWrapper in a filter.
  • Read the cached bytes from HttpServletRequest inside @ControllerAdvice.
  • Consider logging the parsed request object instead of the raw body when that better fits the problem.
  • Be deliberate about character encoding and sensitive-data exposure when logging request bodies.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.