Backend Development 9 min read

Master Spring Boot Request Mapping: Content Types, Params, Headers, URI Patterns

This guide explains how Spring Boot 2.6.12 uses @GetMapping with consumes and produces attributes to restrict request and response content types, demonstrates matching based on request parameters and headers, details the internal request mapping lookup process, compares PathPattern and AntPathMatcher URI patterns, and shows dynamic registration of request handlers.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot Request Mapping: Content Types, Params, Headers, URI Patterns

Environment: Spring Boot 2.6.12

Consumable Content Types

You can narrow the request mapping by specifying the request's content type:

<code>@GetMapping(consumes = MediaType.TEXT_HTML_VALUE)
public Object html() {
    return "html";
}

@GetMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public Object json() {
    return "json";
}</code>

The HTML endpoint matches requests with Content-Type=text/html , while the JSON endpoint matches Content-Type=application/json .

Producible Content Types

<code>@GetMapping(produces = MediaType.TEXT_HTML_VALUE)
public Object html() {
    return "<h1>html</h1>";
}

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public Object json() {
    return "json";
}</code>

The HTML endpoint matches requests with Accept=text/html , and the JSON endpoint matches Accept=application/json .

Matching Parameters or Request Headers

Control the endpoint via request parameters:

<code>@GetMapping(path = "/params", params = {"name=ak"})
public Object params() {
    return "params";
}</code>

This method is selected only when the request contains name=ak .

Control the endpoint via request headers:

<code>@GetMapping(path = "/headers", headers = {"token=123"})
public Object headers() {
    return "headers";
}</code>

This method is selected only when the request includes the header token=123 .

How Spring Finds a Matching Handler

When a request arrives, Spring first looks up a RequestMappingInfo object in AbstractHandlerMethodMapping.mappingRegistry.pathLookup :

<code>public abstract class AbstractHandlerMethodMapping {
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = initLookupPath(request);
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    }
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
    }
    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        for (T mapping : mappings) {
            // From the matched collection, further filter according to each mapping's conditions
            T match = getMatchingMapping(mapping, request);
            if (match != null) {
                matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
            }
        }
    }
}

public abstract class RequestMappingInfoHandlerMapping {
    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
        return info.getMatchingCondition(request);
    }
}

public final class RequestMappingInfo {
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        if (methods == null) return null;
        ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
        if (params == null) return null;
        HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
        if (headers == null) return null;
        ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
        if (consumes == null) return null;
        ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
        if (produces == null) return null;
        // additional checks for path patterns, custom conditions, etc.
        return new RequestMappingInfo(this.name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom, this.options);
    }
}</code>

URI Patterns

PathPattern is a pre‑parsed pattern designed for web use, handling encoding and path variables efficiently. AntPathMatcher is the older string‑based matcher, less efficient and more error‑prone with encoded URLs.

Since Spring 5.3, PathPattern is the recommended solution for Spring MVC (and the only choice for Spring WebFlux). Before that, AntPathMatcher was the default.

PathPattern supports the same syntax as AntPathMatcher and adds capture patterns like {*spring} to match zero or more path segments. It restricts the use of ** to the end of a pattern, reducing ambiguity when selecting the best match.

Example patterns:

/resources/ima?e.png – matches a single character in a segment

/resources/*.png – matches zero or more characters in a segment

/resources/** – matches multiple path segments

/projects/{project}/versions – captures a path variable

/projects/{project:[a-z]+}/versions – captures with a regular expression

Pattern Comparison

When multiple patterns match a URL, Spring selects the best match. Depending on whether PathPattern is enabled, it uses either:

PathPattern.SPECIFICITY_COMPARATOR

AntPathMatcher.getPatternComparator(String path)

Both comparators rank patterns by specificity: fewer URI variables (count 1), single wildcards (count 1), and double wildcards (count 2) make a pattern less specific. If scores tie, the longer pattern wins; if still tied, the pattern with more URI variables wins over wildcards.

The default mapping pattern ( /** ) is excluded from scoring and always sorted last. Prefix patterns like /public/** are considered less specific than patterns without double wildcards.

Dynamic Request Mapping Registration

<code>@Service
public class UserHandler {
    @ResponseBody
    public Object getUsers(@PathVariable("id") String id, HttpServletRequest request) {
        System.out.println(request);
        return "查询用户ID为:" + id;
    }
}

@Configuration
public class MappingConfig {
    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) throws NoSuchMethodException {
        RequestMappingInfo info = RequestMappingInfo.paths("/users/{id}").methods(RequestMethod.GET).build();
        Method method = UserHandler.class.getMethod("getUsers", String.class, HttpServletRequest.class);
        mapping.registerMapping(info, handler, method);
    }
}</code>

Here a RequestMappingInfo object is created with basic metadata, the handler method is obtained via reflection, and the mapping is registered programmatically.

JavaBackend DevelopmentSpring BootREST APIRequest Mapping
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

login 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.