Skip to content

Spring Boot · Web MVC

Request and Response Handling in Spring MVC, Explained

5 min read Updated 2026-06-26 Share:

Practice Request & Response Handling interview questions

Everything between the wire and your method

A controller method looks deceptively simple — an object comes in, an object goes out. Underneath, Spring MVC is converting bytes, matching media types, binding headers, and choosing a status code. Understanding that machinery is what lets you handle uploads, set a Location header, fix a CORS error, or stream a huge export without running out of memory.

HttpMessageConverters are the bridge

Every body — in or out — passes through an HttpMessageConverter. It deserializes the request body for @RequestBody and serializes the return value for @ResponseBody.

@PostMapping("/orders")
Order create(@RequestBody CreateOrderRequest req) {   // JSON bytes → object (read)
    return service.create(req);                       // object → JSON bytes (write)
}

Spring chooses a converter by matching Content-Type (reading) or Accept (writing) against each converter's supported types. Boot registers Jackson for JSON, a string converter for text, a byte converter for binary, plus XML if the dependency is on the classpath. Want a new media type? Add the dependency — don't parse bodies by hand.

On a @RestController, @ResponseBody is implied on every method. You only spell it out on a plain @Controller that mixes data and view-rendering handlers.

Headers and cookies

Read a header with @RequestHeader and a cookie with @CookieValue. Write a header most cleanly through a ResponseEntity builder, and a cookie through a ResponseCookie:

@GetMapping("/dashboard")
ResponseEntity<String> dashboard(
    @CookieValue(value = "session", required = false) String session) {

    ResponseCookie cookie = ResponseCookie.from("theme", "dark")
        .httpOnly(true).secure(true).sameSite("Lax").path("/")
        .maxAge(Duration.ofDays(30)).build();
    return ResponseEntity.ok()
        .header(HttpHeaders.SET_COOKIE, cookie.toString())
        .body("ok");
}

ResponseCookie gives you HttpOnly, Secure, and SameSite — set all three on any session cookie. For headers shared across many endpoints (security headers, server tags), set them once in a filter or interceptor instead of repeating them.

File uploads

Bind the upload to a MultipartFile via @RequestPart on a handler that consumes multipart/form-data:

@PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
ResponseEntity<String> upload(
    @RequestPart("file") MultipartFile file,
    @RequestPart("meta") @Valid AvatarMeta meta) {   // a JSON part — deserialized + validated
    storage.save(file.getOriginalFilename(), file.getBytes());
    return ResponseEntity.ok("stored");
}

@RequestPart beats @RequestParam for mixed bodies because it runs each part through the message converters, so a JSON part can be validated. Cap sizes with spring.servlet.multipart.max-file-size.

The Location header on create

When you create a resource, return 201 with a Location header pointing at it, built from the current request so it stays environment-correct:

URI location = ServletUriComponentsBuilder
    .fromCurrentRequest().path("/{id}")
    .buildAndExpand(saved.id()).toUri();
return ResponseEntity.created(location).body(saved);

Clients can then GET the new resource directly.

CORS

CORS decides which browser origins may call your API. Use @CrossOrigin per handler or a global WebMvcConfigurer:

@Configuration
class CorsConfig implements WebMvcConfigurer {
    @Override public void addCorsMappings(CorsRegistry r) {
        r.addMapping("/api/**")
         .allowedOrigins("https://app.example.com")
         .allowedMethods("GET", "POST", "PUT", "DELETE")
         .allowCredentials(true);
    }
}

Spring answers the browser's preflight OPTIONS automatically. Never combine wildcard origins with allowCredentials(true) — browsers reject it. With Spring Security on the classpath, also permit CORS at the security layer.

Filters vs interceptors

Both wrap requests, but at different layers. A Filter is a servlet-spec component outside Spring MVC that sees every request. A HandlerInterceptor runs inside the DispatcherServlet and only wraps mapped handlers, with access to the resolved handler.

request → Filter(s) → DispatcherServlet → Interceptor.preHandle
        → @Controller method → Interceptor.postHandle → afterCompletion
        → Filter(s) → response

Use a filter for MVC-agnostic concerns that must run first (logging, compression, low-level security); use an interceptor when you need the matched handler or want to limit to MVC routes.

Asynchronous and streaming responses

Returning a CompletableFuture, DeferredResult, or Callable releases the servlet thread while slow work runs, then writes the response on completion — better throughput under I/O-bound load:

@GetMapping("/quote")
CompletableFuture<Quote> quote(@RequestParam String symbol) {
    return quoteService.fetchAsync(symbol);   // container thread freed until resolved
}

For large downloads, stream with a StreamingResponseBody so bytes aren't buffered in memory; for server-pushed events, use an SseEmitter. Each writes on an async thread.

When binding fails

Spring maps binding failures to sensible status codes automatically — 400 for a malformed or invalid body, 415 for an unsupported Content-Type, 406 when Accept can't be met, 405 for the wrong method. You customize the body of those errors with a @RestControllerAdvice (the next page), not by catching exceptions in every handler.

Recap

HttpMessageConverters bridge bytes and objects; @RequestBody/@ResponseBody mark the directions; ResponseEntity and ResponseCookie shape headers and cookies; @RequestPart handles uploads; Location advertises new resources; WebMvcConfigurer configures CORS; filters and interceptors wrap requests at different layers; and CompletableFuture/StreamingResponseBody keep throughput and memory healthy. Master this layer and the HTTP surface of your service stops being a mystery.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel