|
Note: RequestMappingHandlerMapping#setUseSuffixPatternMatch() was deprecated in Spring Framework 5.2.4 to discourage the use of path extensions for request mapping and content negotiation, and it was removed in Spring Framework 7.0. Suffix pattern matching is now always off, so the property is no longer needed and code that still calls the method will not compile against Spring 7. This tutorial is kept as a reference for legacy applications.
In this example, we will understand the effect of the method setUseSuffixPatternMatch() of RequestMappingHandlerMapping. This flag specifies whether to use suffix pattern matching (".*"). In the Spring version used in this example (5.0.x), the default value is true, which means that a handler mapped to /xyz is also mapped to /xyz.abc, /xyz.abc.test, etc. Note that ".*" does not mean that /xyzabc will also match: there has to be a dot right after xyz.
Do not confuse this flag with setUseRegisteredSuffixPatternMatch(), which restricts suffix matching to the file extensions explicitly registered for content negotiation. That flag is covered in the next tutorial.
Status in 2026: deprecated and removed
Suffix pattern matching is a legacy feature, and the API used in this tutorial no longer exists in current Spring versions:
- Spring Framework 5.2.4:
setUseSuffixPatternMatch() and setUseRegisteredSuffixPatternMatch() were deprecated, to discourage the use of path extensions for request mapping and content negotiation (ambiguity, and security issues such as reflected file download attacks).
- Spring Framework 5.3: the default value of
useSuffixPatternMatch changed to false. Suffix matching is now an explicit opt-in.
- Spring Framework 6.0:
PathPatternParser became the default for Spring MVC URL matching. Both suffix options are ignored when a PathPatternParser is used, and they were marked for removal.
- Spring Framework 7.0:
suffixPatternMatch and registeredSuffixPatternMatch were removed completely, together with path-extension content negotiation (favorPathExtension and ignoreUnknownPathExtensions). @RequestMapping(produces="...") should be preferred over suffix pattern matching for mapping requests with acceptable media types derived from the Accept header, or otherwise a query parameter could also be used to express acceptable format(s). The code in this tutorial does not compile against Spring 7.x. In the same release, PathMatcher and AntPathMatcher were deprecated in Spring MVC in favor of PathPatternParser.
The example below is kept as a reference for understanding legacy applications. If you need to serve both and with Spring 7, map the paths explicitly, for example . For content negotiation, use the header or a query parameter (for example ) instead of a file extension.
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 jakarta.servlet.http.HttpServletRequest;
@Controller
public class MyController {
@ResponseBody
@RequestMapping("/employee")
public String employeeHandler(HttpServletRequest request) {
String uri = request.getRequestURI();
return "from employeeHandler, uri: " + uri;
}
}
Java Config:
First, we are going to set useSuffixPatternMatch=true (by default it's set to false, before 5.3 it was set to true by default).
package com.logicbig.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(true);
}
}
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
Output
As seen in the outputs above, all three paths are mapped to our single handler method employeeHandler().
Using useSuffixPatternMatch=false (default)
@Configuration
@ComponentScan
@EnableWebMvc
public class AppConfig
extends WebMvcConfigurerAdapter {
}
As seen above, suffix matching doesn't work anymore: a request with a suffix is no longer mapped to our handler.
Integration Test
useSuffixPatternMatch=true (default)
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.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.mvc.method.annotation.RequestMappingHandlerMapping;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class SuffixMatchTrueTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
private void assertMapped(String uri) throws Exception {
mockMvc
.perform(get(uri))
.andExpect(status().isOk())
.andExpect(handler().handlerType(MyController.class))
.andExpect(handler().methodName(is("employeeHandler")))
.andExpect(content().string(is("from employeeHandler, uri: " + uri)));
}
private void assertNotMapped(String uri) throws Exception {
mockMvc
.perform(get(uri))
.andExpect(status().isNotFound())
.andExpect(content().string(not(containsString(
"from employeeHandler"))));
}
@Test
public void suffixPatternMatchFlagIsEnabled() {
assertThat(
wac.getBean("requestMappingHandlerMapping",
RequestMappingHandlerMapping.class)
.useSuffixPatternMatch(),
is(true));
}
@Test
public void assertMappedUrls() throws Exception {
assertMapped("/employee");
assertMapped("/employee.json");
assertMapped("/employee.xml");
assertMapped("/employee.html");
assertMapped("/employee.txt");
assertMapped("/employee.csv");
assertMapped("/employee.pdf");
assertMapped("/employee.zip");
assertMapped("/employee.JSON");
assertMapped("/employee.xyz");
assertMapped("/employee.details");
assertMapped("/employee.details.abc");
assertMapped("/employee.tar.gz");
assertMapped("/employee.a.b.c.d.e");
}
@Test
public void extensionWithQueryStringIsMapped() throws Exception {
mockMvc
.perform(get("/employee.json?x=1"))
.andExpect(status().isOk())
.andExpect(handler().methodName(is("employeeHandler")))
.andExpect(content().string(is(
"from employeeHandler, uri: /employee.json")));
}
@Test
public void assertNoMappedUrls() throws Exception {
assertNotMapped("/employeeabc");
assertNotMapped("/employees.json");
assertNotMapped("/employee.json/extra");
assertNotMapped("/employee/details");
}
}
mvn clean test -Dtest="SuffixMatchTrueTest" Output$ mvn clean test -Dtest="SuffixMatchTrueTest" [INFO] Scanning for projects... [INFO] [INFO] -------< com.logicbig.example:spring-use-suffix-pattern-example >------- [INFO] Building spring-use-suffix-pattern-example 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-use-suffix-pattern-example --- [INFO] Deleting D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-use-suffix-pattern-example --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\src\main\resources [INFO] [INFO] --- compiler:3.3:compile (default-compile) @ spring-use-suffix-pattern-example --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-use-suffix-pattern-example --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\src\test\resources [INFO] [INFO] --- compiler:3.3:testCompile (default-testCompile) @ spring-use-suffix-pattern-example --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 2 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-use-suffix-pattern-example --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [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.856 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: 3.367 s [INFO] Finished at: 2026-09-24T05:53:32-05:00 [INFO] ------------------------------------------------------------------------ WARNING: No mapping found for HTTP request with URI [/employeeabc] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employees.json] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.json/extra] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee/details] in DispatcherServlet with name ''
useSuffixPatternMatch=false
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.Bean;
import org.springframework.context.annotation.Configuration;
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.mvc.method.annotation.RequestMappingHandlerMapping;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = SuffixMatchFalseTest.LocalConfig.class)
public class SuffixMatchFalseTest {
@Configuration
@EnableWebMvc
public static class LocalConfig {
@Bean
public MyController myController() {
return new MyController();
}
}
@Autowired private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
private void assertMapped(String uri) throws Exception {
mockMvc
.perform(get(uri))
.andExpect(status().isOk())
.andExpect(handler().handlerType(MyController.class))
.andExpect(handler().methodName(is("employeeHandler")))
.andExpect(content().string(is("from employeeHandler, uri: " + uri)));
}
private void assertNotMapped(String uri) throws Exception {
mockMvc
.perform(get(uri))
.andExpect(status().isNotFound())
.andExpect(content().string(not(containsString("from employeeHandler"))));
}
@Test
public void suffixPatternMatchFlagIsDisabled() {
assertThat(
wac.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class)
.useSuffixPatternMatch(),
is(false));
}
@Test
public void assertMappedUrls() throws Exception {
assertMapped("/employee");
}
@Test
public void assertNotMappedUrls() throws Exception {
assertNotMapped("/employee.json");
assertNotMapped("/employee.xml");
assertNotMapped("/employee.html");
assertNotMapped("/employee.txt");
assertNotMapped("/employee.csv");
assertNotMapped("/employee.pdf");
assertNotMapped("/employee.zip");
assertNotMapped("/employee.JSON");
assertNotMapped("/employee.xyz");
assertNotMapped("/employee.details");
assertNotMapped("/employee.details.abc");
assertNotMapped("/employee.tar.gz");
assertNotMapped("/employee.a.b.c.d.e");
assertNotMapped("/employeeabc");
assertNotMapped("/employee/details");
}
@Test
public void extensionWithQueryStringIsNotMapped() throws Exception {
assertNotMapped("/employee.json?x=1");
}
@Test
public void exactPathWithQueryStringIsMapped() throws Exception {
mockMvc
.perform(get("/employee?x=1"))
.andExpect(status().isOk())
.andExpect(handler().methodName(is("employeeHandler")))
.andExpect(content().string(is("from employeeHandler, uri: /employee")));
}
}
mvn clean test -Dtest="SuffixMatchFalseTest" Output$ mvn clean test -Dtest="SuffixMatchFalseTest" [INFO] Scanning for projects... [INFO] [INFO] -------< com.logicbig.example:spring-use-suffix-pattern-example >------- [INFO] Building spring-use-suffix-pattern-example 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] --- clean:3.2.0:clean (default-clean) @ spring-use-suffix-pattern-example --- [INFO] Deleting D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ spring-use-suffix-pattern-example --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\src\main\resources [INFO] [INFO] --- compiler:3.3:compile (default-compile) @ spring-use-suffix-pattern-example --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 3 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target\classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-use-suffix-pattern-example --- [INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\src\test\resources [INFO] [INFO] --- compiler:3.3:testCompile (default-testCompile) @ spring-use-suffix-pattern-example --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 2 source files to D:\example-projects\spring-mvc\handler-mapping\spring-use-suffix-pattern-example\target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ spring-use-suffix-pattern-example --- [INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.SuffixMatchFalseTest [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.563 s -- in com.logicbig.example.SuffixMatchFalseTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.737 s [INFO] Finished at: 2026-09-24T05:59:40-05:00 [INFO] ------------------------------------------------------------------------ INFO: Skipping bean definition for [BeanMethod:name=myController,declaringClass=com.logicbig.example.SuffixMatchTrueTest$LocalConfig]: a definition for bean 'myController' already exists. This top-level bean definition is considered as an override. WARNING: No mapping found for HTTP request with URI [/employee.json] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.json] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.xml] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.html] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.txt] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.csv] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.pdf] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.zip] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.JSON] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.xyz] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.details] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.details.abc] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.tar.gz] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee.a.b.c.d.e] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employeeabc] in DispatcherServlet with name '' WARNING: No mapping found for HTTP request with URI [/employee/details] in DispatcherServlet with name ''
Example ProjectDependencies and Technologies Used: - spring-webmvc 6.0.0 (Spring Web MVC)
Version Compatibility: 4.0.3.RELEASE - 6.2.19 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 6.0.0 (Spring TestContext Framework)
- junit-jupiter-engine 5.8.2 (Module "junit-jupiter-engine" of JUnit 5)
- jakarta.servlet-api 6.0.0 (Jakarta Servlet API documentation)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- JDK 17
- Maven 3.9.11
|