In this tutorial, we will understand the effect of the setUseRegisteredSuffixPatternMatch() method of RequestMappingHandlerMapping. This flag specifies whether suffix pattern matching (last tutorial) should work only against path extensions explicitly registered with the ContentNegotiationManager (check out the content negotiation tutorial). By default, this flag is set to "false". Setting it to "true" also enables suffix pattern matching itself, but restricted to the registered extensions.
Legacy feature: suffix pattern matching, including setUseRegisteredSuffixPatternMatch(), was deprecated in Spring Framework 5.2.4 and removed completely in Spring Framework 7.0. The example below was written for Spring Framework 5.0.x and cannot run unchanged on Spring 7. It remains useful for understanding the behavior of older applications and for migrating them. See the migration notes at the end of this page.
Example
A controller
package com.logicbig.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class MyController {
@ResponseBody
@RequestMapping("/employee")
public String employeeHandler(HttpServletRequest request) {
String uri = request.getRequestURI();
if (uri.endsWith(".xml")) {
return "<doc>test response at " + uri + "</doc>";
}
return "plain test response at " + uri;
}
}
Java Config
First, let's see the default behavior, where setUseRegisteredSuffixPatternMatch is set to false.
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
}
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
Output
As the outputs above show, all three paths are mapped to our single handler method, employeeHandler(), and the path with the .xml extension is served with an XML media type. That happens because of ServletPathExtensionContentNegotiationStrategy (tutorial here), which was enabled by default in Spring 5.0. In Spring 5.3 and later, both suffix pattern matching and path extension content negotiation are disabled by default, so these outputs would be different there.
Let's say we want to match only the URIs '/employee' and '/employee.xml' and reject all other suffixes. For that, we have to set setUseRegisteredSuffixPatternMatch to true. The .xml extension is registered with the ContentNegotiationManager, so it still matches, whereas unregistered extensions such as .abc no longer do.
Setting setUseRegisteredSuffixPatternMatch to true
Let's configure RequestMappingHandlerMapping with the desired setting:
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true);
}
}
With the flag enabled, '/employee' and '/employee.xml' are still mapped to the handler method. A request with an unregistered extension, such as '/employee.details.abc', is no longer matched, and Tomcat responds with an error page (HTTP 404).
Integration Tests
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {SuffixMatchFalseTest.DefaultAppConfig.class,
MyController.class})
public class SuffixMatchFalseTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Configuration
@EnableWebMvc
public static class DefaultAppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
//starting 5.3 UseSuffixPatternMatch was turned to false by default
configurer.setUseSuffixPatternMatch(true);
}
}
@Test
public void testEmployeeNoExtension() throws Exception {
mockMvc
.perform(get("/employee"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN))
.andExpect(content().string("plain test response at /employee"));
}
@Test
public void testEmployeeXml() throws Exception {
mockMvc
.perform(get("/employee.xml"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_XML))
.andExpect(content().string("<doc>test response at " + "/employee.xml</doc>"));
}
@Test
public void testEmployeeJson() throws Exception {
mockMvc
.perform(get("/employee.details"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN))
.andExpect(content().string("plain test response at " + "/employee.details"));
}
@Test
public void testEmployeeHtml() throws Exception {
mockMvc
.perform(get("/employee.html"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andExpect(content().string(
"plain test response at /employee.html"));
}
}
mvn clean test -Dtest="SuffixMatchFalseTest" Output$ mvn clean test -Dtest="SuffixMatchFalseTest" [INFO] Scanning for projects... [INFO] [INFO] --< com.logicbig.example:spring-use-registered-suffix-pattern-match >--- [INFO] Building spring-use-registered-suffix-pattern-match 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-use-registered-suffix-pattern-match --- [INFO] Deleting D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-use-registered-suffix-pattern-match --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\main\resources [INFO] [INFO] --- compiler:3.3:compile (default-compile) @ spring-use-registered-suffix-pattern-match --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target\classes [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/main/java/com/logicbig/example/AppConfig.java: D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\main\java\com\logicbig\example\AppConfig.java uses or overrides a deprecated API. [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/main/java/com/logicbig/example/AppConfig.java: Recompile with -Xlint:deprecation for details. [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-use-registered-suffix-pattern-match --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\test\resources [INFO] [INFO] --- compiler:3.3:testCompile (default-testCompile) @ spring-use-registered-suffix-pattern-match --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 2 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target\test-classes [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/test/java/com/logicbig/example/SuffixMatchFalseTest.java: D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\test\java\com\logicbig\example\SuffixMatchFalseTest.java uses or overrides a deprecated API. [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/test/java/com/logicbig/example/SuffixMatchFalseTest.java: Recompile with -Xlint:deprecation for details. [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-use-registered-suffix-pattern-match --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.SuffixMatchFalseTest [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.637 s -- in com.logicbig.example.SuffixMatchFalseTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.508 s [INFO] Finished at: 2026-09-25T03:09:34-05:00 [INFO] ------------------------------------------------------------------------ INFO: Completed initialization in 1 ms INFO: Completed initialization in 0 ms INFO: Completed initialization in 1 ms INFO: Completed initialization in 0 ms
package com.logicbig.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class SuffixMatchTrueTest {
@Autowired private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void testEmployeeNoExtension() throws Exception {
mockMvc
.perform(get("/employee"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN))
.andExpect(content().string("plain test response at /employee"));
}
@Test
public void testEmployeeXml() throws Exception {
mockMvc
.perform(get("/employee.xml"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_XML))
.andExpect(content().string("<doc>test response at " + "/employee.xml</doc>"));
}
@Test
public void testEmployeeJsonShouldNotMatch() throws Exception {
mockMvc.perform(get("/employee.details")).andExpect(status().isNotFound());
}
@Test
public void testEmployeeHtmlShouldNotMatch() throws Exception {
mockMvc.perform(get("/employee.html")).andExpect(status().isNotFound());
}
}
mvn clean test -Dtest="SuffixMatchTrueTest" Output$ mvn clean test -Dtest="SuffixMatchTrueTest" [INFO] Scanning for projects... [INFO] [INFO] --< com.logicbig.example:spring-use-registered-suffix-pattern-match >--- [INFO] Building spring-use-registered-suffix-pattern-match 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-use-registered-suffix-pattern-match --- [INFO] Deleting D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-use-registered-suffix-pattern-match --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\main\resources [INFO] [INFO] --- compiler:3.3:compile (default-compile) @ spring-use-registered-suffix-pattern-match --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target\classes [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/main/java/com/logicbig/example/AppConfig.java: D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\main\java\com\logicbig\example\AppConfig.java uses or overrides a deprecated API. [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/main/java/com/logicbig/example/AppConfig.java: Recompile with -Xlint:deprecation for details. [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-use-registered-suffix-pattern-match --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\test\resources [INFO] [INFO] --- compiler:3.3:testCompile (default-testCompile) @ spring-use-registered-suffix-pattern-match --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 2 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\target\test-classes [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/test/java/com/logicbig/example/SuffixMatchFalseTest.java: D:\example-projects\spring-mvc\handler-mapping\spring-use-registered-suffix-pattern-match\src\test\java\com\logicbig\example\SuffixMatchFalseTest.java uses or overrides a deprecated API. [INFO] /D:/LogicBig/example-projects/spring-mvc/handler-mapping/spring-use-registered-suffix-pattern-match/src/test/java/com/logicbig/example/SuffixMatchFalseTest.java: Recompile with -Xlint:deprecation for details. [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-use-registered-suffix-pattern-match --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.SuffixMatchTrueTest [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.573 s -- in com.logicbig.example.SuffixMatchTrueTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.837 s [INFO] Finished at: 2026-09-25T03:15:12-05:00 [INFO] ------------------------------------------------------------------------ INFO: Completed initialization in 0 ms WARNING: No mapping for GET /employee.html INFO: Completed initialization in 0 ms INFO: Completed initialization in 1 ms WARNING: No mapping for GET /employee.details INFO: Completed initialization in 0 ms
How to enable it in 5.3.2+ and 6.0.0+?
package com.logicbig.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
import java.util.Map;
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(null);
configurer.setUseRegisteredSuffixPatternMatch(true);
configurer.setUseSuffixPatternMatch(true);
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// Instantiate the deprecated strategy
var strategy =
new PathExtensionContentNegotiationStrategy(
Map.of("xml", MediaType.APPLICATION_XML));
strategy.setUseRegisteredExtensionsOnly(true);
// Force Spring 6 to include it in the negotiation pipeline
configurer.strategies(List.of(strategy));
}
@Bean
public MyController myController() {
return new MyController();
}
}
Complete example is here
Migrating to Spring Framework 6 and 7
Suffix pattern matching was deprecated in Spring Framework 5.2.4 and removed in Spring Framework 7.0, together with setUseSuffixPatternMatch(), the path extension content negotiation strategies, and the related favorPathExtension option. Code that still calls setUseRegisteredSuffixPatternMatch() does not compile against Spring 7 and should be updated. In Spring 7, a URI is matched exactly as mapped, so a handler mapped to '/employee' does not match '/employee.xml'. Here are the recommended replacements:
1. Map each URI explicitly, for example @GetMapping({"/employee", "/employee.xml"}).
2. Let clients choose the representation with the Accept request header, which is the default content negotiation strategy.
3. Use the format query parameter. Enable it in the MVC Java config, as follows:
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.favorParameter(true)
.mediaType("xml", MediaType.APPLICATION_XML);
}
}
With this configuration, a request such as '/employee?format=xml' is served with an XML media type.
Example ProjectDependencies and Technologies Used: - spring-webmvc 5.3.0 (Spring Web MVC)
Version Compatibility: 4.0.3.RELEASE - 5.3.1
- spring-test 5.3.0 (Spring TestContext Framework)
- junit-jupiter-engine 5.0.0 (Module "junit-jupiter-engine" of JUnit 5)
- javax.servlet-api 3.0.1 (Java Servlet API)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- JDK 1.8
- Maven 3.9.11
|