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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot 3 API Versioning with Headers, Params, and Consumes

1. Introduction

Spring MVC’s annotation‑driven controller model already supports rich attributes on @RequestMapping‑derived annotations. By using headers, params, and consumes you can implement clean, maintainable API version control without extra complexity.

2. Practical Cases

2.1 Annotation Overview

The core attributes of @RequestMapping relevant to versioning are listed below.

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 {};
}

2.2 Using headers attribute

Define two methods with the same path but different required header values.

@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"; }
}

Only requests that contain the matching X-API-Version header are routed to the corresponding method.

2.3 Using params attribute

@GetMapping(value = "", params = {"v=v1"})
public Object v1() { return "User Param v1"; }

@GetMapping(value = "", params = {"v=v2"})
public Object v2() { return "User Param v2"; }

The request must contain a query parameter v with value v1 or v2 to 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 HttpMessageConverter for the custom media type.

@GetMapping(value = "", consumes = {"pack/v1"})
public Object v1() { return "User ContentType v1"; }

@GetMapping(value = "", consumes = {"pack/v2"})
public Object v2() { return "User ContentType v2"; }

When the same endpoints are changed to POST with a request body, the default MappingJackson2HttpMessageConverter only supports application/json. Because the request uses Content‑Type: pack/v1, Spring throws HttpMediaTypeNotSupportedException.

@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;
}

Both calls initially return 415. To support the custom media type we register a converter that declares pack/* as supported.

@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);
    }
}

After adding the custom converter, POST requests with Content‑Type: pack/v1 or pack/v2 are processed correctly.

Thus, using headers, params, and consumes together with a custom HttpMessageConverter enables clean multi‑version API control in Spring Boot 3.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

API VersioningSpring BootHeadersRequestMappingHttpMessageConverterConsumesParams
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.