Skip to content

Documentation should clarify unversioned endpoints "match any version" only if not superseded #36082

@kumar-github

Description

@kumar-github

I am trying to understand the new API Versioning feature introduced in Spring 7 and seems like something is not right. According to the spring documentation, if versioning is enabled and no version attribute is specified in the handler method, then it is considered (unversioned) as a match to any (supported) version.

Below is from Spring Reference Doc

Once API versioning is enabled, you can begin to map requests with versions. The @RequestMapping version attribute supports the following:

No value — matches any version

Fixed version ("1.2") — matches the given version only

Baseline version ("1.2+") — matches the given version and above

Check the attached controller and the properties file.

SampleController.java

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/{version}/data")
public class SampleController {

    @GetMapping
    public String unversioned() {
        return "unversioned";
    }

    @GetMapping(version = "v1.0.0")
    public String version1() {
        return "version 1";
    }
}

application.properties

trace                                  = true

spring.mvc.apiversion.use.path-segment = 1

// added this just to explain the behaviour
spring.mvc.apiversion.supported        = 100, 200

I have implemented a URI path based versioning with two handler methods. One with a specific version and the other without any version (no version attribute specified).

So I expect the below requests should behave as follows:
Request-1:
http://localhost:8080/api/v1/data --> should return "version 1", as it is an exact match

Request-2:
http://localhost:8080/api/v100/data --> should return "unversioned", as there is no exact version match, and the unversioned() method should be called but it is not, instead it returns a 400 Bad Request.

Response: There was an unexpected error (type=Bad Request, status=400).

I did a little bit of debugging and found that the lookupHandlerMethod() method in the AbstractHandlerMethodMapping.java is getting two matches for the request GET /api/v100/data and selecting the handler method with version attribute specified as the best match which is not correct.

Matches:

  1. {GET [/api/{version}/data]} --> should consider this as best match
  2. {GET [/api/{version}/data], version [v1.0.0]} --> this is not the best match but the framework picks this as best match.

Console log:

2025-12-29T16:34:36.750+05:30 TRACE 88093 --- [spring-boot-api-versioning-101] [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet        : GET "/api/v100/data", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2025-12-29T16:34:36.752+05:30 TRACE 88093 --- [spring-boot-api-versioning-101] [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : 2 matching mappings: [{GET [/api/{version}/data], version [v1.0.0]}, {GET [/api/{version}/data]}]
2025-12-29T16:34:36.752+05:30  WARN 88093 --- [spring-boot-api-versioning-101] [nio-8080-exec-4] .w.s.m.a.ResponseStatusExceptionResolver : Resolved [org.springframework.web.accept.NotAcceptableApiVersionException: 400 BAD_REQUEST "Invalid API version: '100.0.0'."]
Image Image

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: documentationA documentation task

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions