Explain Codes LogoExplain Codes Logo

How to respond with an HTTP 400 error in a Spring MVC @ResponseBody method returning String

java
response-entity
http-status-codes
exception-handling
Anton ShumikhinbyAnton Shumikhin·Nov 3, 2024
TLDR

To issue an HTTP 400 error in a Spring MVC method, make use of the ResponseEntity class to formulate the response:

import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ExampleController { @GetMapping("/bakery") public ResponseEntity<String> getBread() { // Evaluate The bread's freshness if (/* stale bread */) { // No one likes stale bread return ResponseEntity.badRequest().body("That's a lame slice of bread, bro"); } // Proceed normally, the bread is fresh and tasty return ResponseEntity.ok("Get your hot bread right from the oven!"); } }

Check the situation, return ResponseEntity.badRequest().body("That's a lame slice of bread, bro") for errors, or ResponseEntity.ok("Get your hot bread right from the oven!") for success.

Looking closer at ResponseEntity

The ResponseEntity class offers powerful manipulation over the HTTP response details. It has been enriched after Spring 4.1 with builder methods for more expressive response production.

It's crucial to think beyond just OK and BAD_REQUEST. The HttpStatus enum holds many other statuses like HttpStatus.NOT_FOUND. Choose the one that fits your specific situation.

Exception handling beyond method scope is also a thing. @ExceptionHandler combined with @ResponseStatus can catch and handle exceptions thrown across your Spring application.

HttpStatus and custom exceptions linking

Adopting a separation of concerns approach, you should aim to keep controllers relatively slim, shifting most of the exception handling to separate handlers or methods marked with @ExceptionHandler.

@ResponseStatus(HttpStatus.BAD_REQUEST) class FreshBreadMissingException extends RuntimeException { public FreshBreadMissingException(String message) { super(message); } } @ExceptionHandler(FreshBreadMissingException.class) public ResponseEntity<String> handleCustomException(FreshBreadMissingException ex) { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body(ex.getMessage()); }

Throwing an instance of FreshBreadMissingException will result in a response with a HTTP 400 status code and the error message you provide. Clear and reusable.

JSON and Spring's object mapping

Ensuring that Jackson’s library is in your classpath is vital when working with JSON. Spring makes use of MappingJackson2HttpMessageConverter for object serialization.

It's good practice to differentiate between domain objects and Data Transfer Objects (DTOs). It allows for an API designed with consumer needs in mind, not your data model.

Advanced Handling

Spring MVC provides different ways of managing response status codes. Let's dive a bit deeper:

Manual Setting of Status Code: You can manually set the status code via HttpServletResponse:

import javax.servlet.http.HttpServletResponse; // Inside your @RequestMapping method: response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // "That's no good, mate!"

Spring’s Exception System Utilization: Spring transforms exceptions into HTTP responses. Custom exceptions with @ResponseStatus annotation will trigger Spring MVC to return that specific status:

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Hey man, that's just like, your opinion") public class NotCoolEnoughException extends RuntimeException { // Your implementation here }

Having the Right Media Type: Ensure your controller can produce the correct media type, frequently JSON:

@GetMapping(value = "/bakery", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<String> getBread() { // ... Method implementation ... }

DTO Factories for Consistency: Create factories that return DTOs with a standardized format. This ensures a consistent JSON format:

// Example code for a DTO factory method public ResponseEntity<ComplaintDto> buildBadRequestResponse(String complaintMessage) { ComplaintDto complaintDto = new ComplaintDto(complaintMessage, /*other details*/); return new ResponseEntity<>(complaintDto, HttpStatus.BAD_REQUEST); }