Master Spring Boot 3 API Versioning with Headers, Params, and Consumes
This article demonstrates how to implement multi‑version REST APIs in Spring Boot 3 by leveraging @RequestMapping attributes such as headers, params, and consumes, providing complete code examples, request screenshots, and a custom HttpMessageConverter for Content‑Type based versioning.
1. Introduction
Spring MVC’s annotation‑driven controller model already supports rich attributes on @RequestMapping‑derived annotations. By using
headers,
params, and
consumesyou can implement clean, maintainable API version control without extra complexity.
2. Practical Cases
2.1 Annotation Overview
The core attributes of
@RequestMappingrelevant to versioning are listed below.
<code>public @interface RequestMapping {
/**
* Limit mapping by request parameters, e.g. "myParam=myValue" or "myParam!=myValue".
*/
String[] params() default {};
/**
* Limit mapping by request headers, e.g. "MyHeader=myValue" or "MyHeader!=myValue".
*/
String[] headers() default {};
/**
* Limit mapping by consumed media types, e.g. "text/plain" or "application/*".
*/
String[] consumes() default {};
}</code>2.2 Using headers attribute
Define two methods with the same path but different required header values.
<code>@RestController("userController2")
@RequestMapping("/users2")
public class UserController {
@GetMapping(value = "", headers = {"X-API-Version=v1"})
public Object v1() { return "User Header v1"; }
@GetMapping(value = "", headers = {"X-API-Version=v2"})
public Object v2() { return "User Header v2"; }
}</code>Only requests that contain the matching
X-API-Versionheader are routed to the corresponding method.
2.3 Using params attribute
<code>@GetMapping(value = "", params = {"v=v1"})
public Object v1() { return "User Param v1"; }
@GetMapping(value = "", params = {"v=v2"})
public Object v2() { return "User Param v2"; }</code>The request must contain a query parameter
vwith value
v1or
v2to match the appropriate method.
2.4 Using consumes attribute
For GET requests the attribute works similarly, but for POST requests with a body you must provide a suitable
HttpMessageConverterfor the custom media type.
<code>@GetMapping(value = "", consumes = {"pack/v1"})
public Object v1() { return "User ContentType v1"; }
@GetMapping(value = "", consumes = {"pack/v2"})
public Object v2() { return "User ContentType v2"; }</code>When the same endpoints are changed to POST with a request body, the default
MappingJackson2HttpMessageConverteronly supports
application/json. Because the request uses
Content‑Type: pack/v1, Spring throws
HttpMediaTypeNotSupportedException.
<code>@PostMapping(value = "", consumes = {"pack/v1"})
public Object v1(@RequestBody User user) {
user.setName(user.getName() + " - V1");
return user;
}
@PostMapping(value = "", consumes = {"pack/v2"})
public Object v2(@RequestBody User user) {
user.setName(user.getName() + " - V2");
return user;
}</code>Both calls initially return 415. To support the custom media type we register a converter that declares
pack/*as supported.
<code>@Component
public class MultiVersion2Config implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(new MediaType("pack", "*"));
messageConverter.setSupportedMediaTypes(supportedMediaTypes);
converters.add(messageConverter);
}
}</code>After adding the custom converter, POST requests with
Content‑Type: pack/v1or
pack/v2are processed correctly.
Thus, using
headers,
params, and
consumestogether with a custom
HttpMessageConverterenables clean multi‑version API control in Spring Boot 3.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.