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