This is a living document to provide a central place for common Spring Web MVC use cases. With this Spring MVC cheat sheet, you get a quick reference to solve re-occurring tasks for your endpoints like validation, content-negotiation, file-handling, etc.
If not stated differently, the examples assume your application uses the spring-boot-starter-web
dependency and at least Java 9. Furthermore, you should at least use a Spring Boot version greater than 2.1. You can find the source code for these Spring Web MVC examples on GitHub.
Download the PDF version of this cheat sheet here.
How can I return JSON payload from a Spring MVC endpoint?
To return JSON payload from your Spring MVC endpoint, ensure your project is using the Spring Boot Starter Web. Using this Starter, Spring Boot ensures to auto-configure Jackson to serialize and deserialize Java objects to JSON.
Next, annotate your controller with @RestController
to instruct Spring to bind the response of your method to the web response. Finally, specify the return type of your method using the produces
attribute of your mapping (e.g. @GetMapping
, @RequestMapping
, etc.) annotation:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RestController @RequestMapping("/json") public class JsonPayloadController { @GetMapping(path = "/orders", produces = MediaType.APPLICATION_JSON_VALUE) public List<Order> getOrders() { List<Order> orders = List.of( new Order("42L", Set.of("foreign", "books"), 1L, LocalDateTime.now()), new Order("58B", Set.of("fractile", "computer"), 48L, LocalDateTime.now()) ); return orders; } } |
How to return a different HTTP status for a Spring MVC controller?
To return a different HTTP status from your controller, ensure your method returns the response wrapper ResponseEntity
from Spring. You can specify the data type of the HTTP body as a type parameter, e.g. ResponseEntity<Order>
to ensure type safety.
The ResponseEntity
class then provides static methods to build the response according to your needs. This allows you to specify the HTTP status of the response manually:
1 2 3 4 5 6 7 8 |
@GetMapping(path = "/orders", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<Order>> getOrders() { List<Order> orders = List.of( new Order("42L", Set.of("foreign", "books"), 1L, LocalDateTime.now()), new Order("58B", Set.of("fractile", "computer"), 48L, LocalDateTime.now()) ); return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body(orders); } |
How can I validate the incoming payload to my controller?
To validate incoming payload, you can make use of the Bean Validation API. If your project contains the Spring Boot Starter Web or WebFlux, you don't need any further dependencies as both depend on spring-boot-starter-validation
.
Let's start with validating path variables ("/myPathVariable"
) and query parameters ("?q=duke&size=10"
). For this, you need to annotate your controller with @Validated
and add the Bean Validation annotations (coming from javax.validation
or jakarta.validation
) to your method arguments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Validated @RestController @RequestMapping("/validation") public class ValidationController { @GetMapping("/{message}") public String validateParameters( @PathVariable("message") @Size(min = 5, max = 10) String message, @RequestParam("size") @Positive Long size) { return "Query parameters where valid"; } } |
The example above will validate that the path variable has between 5 and 10 characters and that the query parameter size
is a positive number. Sending an invalid request to this endpoint, like:
1 |
curl -v http://localhost:8080/validation/foo?size=-42 |
results by default in an HTTP status 500. If you want to change this behavior, you can add an ExceptionHandler
to catch the ConstraintViolationException
and return HTTP status 400 (Bad Request) instead:
1 2 3 4 5 |
@ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) { return new ResponseEntity<>("Validation Error: " + e.getMessage(), HttpStatus.BAD_REQUEST); } |
Either add this to your controller class or to a class annotated with @ControllerAdvice
.
Validating an incoming HTTP request body payload works a little bit differently. First, ensure you use the Bean Validation annotations on the POJO that maps to the incoming request body:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Payload { @NotBlank private String message; @Email private String email; @Future private LocalDateTime memberSince; // getters & setters } |
Next, add @Valid
to the @RequestBody
annotation of your controller method:
1 2 3 4 |
@PostMapping public String validatePayload(@Valid @RequestBody Payload payload) { return "Payload is valid"; } |
Sending an payload will result in an HTTP 400 response including information about validation errors:
1 2 3 |
curl -X POST -H "Content-Type: application/json" \ -d '{"message": "Hello", "email": "notAnEmail", "memberSince": "2025-04-20T12:00:00"}' \ http://localhost:8080/validation |
More information on validating payload can be found here:
- Bean Validation specification
- List of all built-in validation annotations
How can I return a Thymeleaf view from a controller?
To start using Thymeleaf, add the following Spring Boot Starter to your project:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> |
With this Starter, you'll get the correct Thyemleaf dependency and the autoconfiguration mechanism of Spring Boot ensures all required beans with the correct configuration are in place.
Next, you need an endpoint to return your Thyemleaf view. For this use @Controller
to annotate your controller class (hint: you can't use @RestController
here, as this returns the payload as part of the HTTP body). There are multiple ways to tell Spring which view name to render. The simplest is to return the name of the view as a String
:
1 2 3 4 5 6 7 8 9 10 |
@Controller @RequestMapping("/welcome") public class ViewController { @GetMapping public String getWelcomePage(Model model) { model.addAttribute("message", "Hello World!"); return "welcome"; } } |
You can inject the Model
as a method argument and set any data you need to render your view.
By default convention, the Thymeleaf view resolver searches for templates at classpath:/templates/
with the .html
suffix. Therefore we can place our templates inside src/main/resources/templates
:
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE HTML> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <title>Welcome</title> </head> <body> <div> <span th:text="${message}"></span> </div> </body> </html> |
The view is now accessible at http://localhost:8080/welcome
and returns an HTML file including the message we set inside our controller.
More information on using Thymeleaf with Spring can be found here:
- Thymeleaf documentation
- Spring Guide for creating a Hello World website with Spring
- Spring Boot documentation on templating Engines
How can I return XML from my Spring MVC controller?
To return XML from your controller, you need an additional dependency from Jackson:
1 2 3 4 |
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> |
If you are using the Spring Boot Starter Web, Spring Boot ensure to auto-configure everything to make use of the JacksonXmlModule
. What's left is to annotate your POJO with @XmlRootElement
:
1 2 3 4 5 6 7 8 9 10 11 |
@XmlRootElement public class Order { private String id; private Set<String> tags; private Long customerId; private LocalDateTime orderAt; // getters & setters } |
Now you can return XML from your Spring MVC controller methods:
1 2 3 4 5 6 7 8 |
@GetMapping(path = "/orders", produces = MediaType.APPLICATION_XML_VALUE) public List<Order> getOrders() { List<Order> orders = List.of( new Order("42L", Set.of("foreign", "books"), 1L, LocalDateTime.now()), new Order("58B", Set.of("fractile", "computer"), 48L, LocalDateTime.now()) ); return orders; } |
How can I provide different content types for a controller?
First, make sure you have the corresponding HttpMessageConverter
enabled. If you use the Spring Boot Starter Web, you'll get a converter for JSON configured, as the Jackson dependency is pulled with this starter. For XML, make sure to read the section above. Besides JSON and XML, Spring also provides a converter for String
orbyte
by default.
To now be able to return different content types and let the client decide which type he can process (content negotiation), pass multiple MediaType
definitions to the produces
field of your mapping annotation. This might be @GetMapping
,@RequestMapping
, @PostMapping
, etc:
1 2 3 4 5 6 7 8 |
@GetMapping(path = "/orders", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}) public List<Order> getOrders() { List<Order> orders = List.of( new Order("42L", Set.of("foreign", "books"), 1L, LocalDateTime.now()), new Order("58B", Set.of("fractile", "computer"), 48L, LocalDateTime.now()) ); return orders; } |
A client can now decide which content type he wants to process using the HTTP Accept
header:
1 2 3 |
curl -H "Accept: application/json" http://localhost:8080/mix/orders [{"id":"42L","tags":["books","foreign"],"customerId":1,"orderAt":"2020-04-06T05:45:39.40289"}, {"id":"58B","tags":["computer","fractile"],"customerId":48,"orderAt":"2020-04-06T05:45:39.402908"}] |
1 2 3 |
curl -H "Accept: application/xml" http://localhost:8080/mix/orders <List><item><id>42L</id><tags><tags>books</tags><tags>foreign</tags></tags><customerId>1</customerId><orderAt>2020-04-06T05:48:08.077563</orderAt></item> <item><id>58B</id><tags><tags>computer</tags><tags>fractile</tags></tags><customerId>48</customerId><orderAt>2020-04-06T05:48:08.077587</orderAt></item></List> |
How can I upload a file with Spring Web MVC?
To upload a file to a Spring Web MVC controller endpoint, you can make use of Spring's MultipartFile
class. A common approach to upload files is using an HTML form
and the content type multipart/form-data
.
Using this content type and file
as a key, you can access your file in the backend as the following:
1 2 3 4 5 6 7 8 9 10 |
@PostMapping public void upload(@RequestParam("file") MultipartFile multipartFile) throws IOException { System.out.println("Uploaded new file"); System.out.println("Filename: " + multipartFile.getOriginalFilename()); System.out.println("Size: " + multipartFile.getSize()); byte[] content = multipartFile.getBytes(); } |
For a more full-stack example, consider reading my Up- and download files with React and Spring Boot blog post.
How can I download a file with Spring Web MVC?
For an example on how to download a file, let's use a file available on the classpath (part of src/main/resources
). To instruct a client e.g. a browser that the response is a file, you have to set some HTTP headers.
Using Content-Type
you help a client to understand which kind of file is downloaded to e.g. suggest opening the file with a PDF viewer. Furthermore, with Content-Length
and Content-Disposition
you can add metadata like the filename and size to the response:
1 2 3 4 5 6 7 8 9 10 11 12 |
@GetMapping public ResponseEntity<byte[]> download() throws IOException { byte[] pdfContent = this.getClass().getResourceAsStream("/document.pdf").readAllBytes(); HttpHeaders header = new HttpHeaders(); header.setContentType(MediaType.APPLICATION_PDF); header.setContentLength(pdfContent.length); header.set("Content-Disposition", "attachment; filename=document.pdf"); return new ResponseEntity(pdfContent, header, HttpStatus.OK); } |
For a more full-stack example, consider reading my Up- and download files with React and Spring Boot blog post.
How can I use Spring Web MVC and WebFlux together?
If you plan to make use of the Spring WebFlux WebClient
for making concurrent HTTP requests using the Reactor operators, simply include the spring-boot-starter-webflux
to your project:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> |
You can easily combine this with your existing Spring Web MVC application, as Spring Boot will still auto-configure the embedded Tomcat for you. Hence you can use your existing (blocking) Web MVC application and start using parts of WebFlux for requesting data from remote services.
Download the PDF version of this cheat sheet here.
Have fun using this Spring Web MVC cheat sheet,
Phil