You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Spring(Vanilla)迁移至Spring Boot:如何保留带.s/.json后缀的URL映射

问题场景与需求

正在把原生Spring MVC的大型Web应用迁移到Spring Boot 3.2.5,原应用在web.xml里配置了DispatcherServlet只处理.s和.json后缀的请求:

<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <load-on-startup>3</load-on-startup>
</servlet> 
<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>*.s</url-pattern>
</servlet-mapping>
<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>*.json</url-pattern>
</servlet-mapping>

Spring Boot里DispatcherServlet默认映射"/",但应用里大量URL引用(比如https://example.com/article.s?id={id})和控制器代码都依赖这些后缀。现在想不用修改所有URL和控制器代码,让/article.s能匹配@RequestMapping(value = "/article", params="id")的控制器,.json后缀的请求同理——原Spring MVC里不用在@RequestMapping加后缀,框架会自动忽略。

另外,旧版Spring用WebMvcConfigurer.configurePathMatch里的setUseSuffixPatternMatch(true)实现这个功能,但Spring 6里这方法已经废弃且无替代,只想实现把*.s/*.json重写成对应无后缀路径的效果。

解决方案

方案1:自定义请求路径剥离Filter

写一个Filter,在请求到达DispatcherServlet之前,把.s或.json后缀去掉,修改请求URI:

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SuffixStripFilter implements Filter {

    private static final Pattern SUFFIX_PATTERN = Pattern.compile("(.+)(\\.s|\\.json)$");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();
        Matcher matcher = SUFFIX_PATTERN.matcher(requestURI);

        if (matcher.matches()) {
            String strippedUri = matcher.group(1);
            HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
                @Override
                public String getRequestURI() {
                    return strippedUri;
                }

                @Override
                public String getServletPath() {
                    return strippedUri;
                }
            };
            chain.doFilter(wrappedRequest, response);
        } else {
            chain.doFilter(request, response);
        }
    }
}

这个Filter会自动匹配带目标后缀的请求,剥离后再传给DispatcherServlet,控制器不需要做任何修改就能匹配。

方案2:用UrlRewriteFilter实现URL重写

引入成熟的URL重写工具,配置更灵活:

  1. 添加Maven依赖:
<dependency>
    <groupId>org.tuckey</groupId>
    <artifactId>urlrewritefilter</artifactId>
    <version>4.0.4</version>
</dependency>
  1. 在src/main/webapp/WEB-INF下创建urlrewrite.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
        "https://tuckey.org/res/dtds/urlrewrite4.0.dtd">
<urlrewrite>
    <rule>
        <from>^(.+)\.s$</from>
        <to type="forward">$1</to>
    </rule>
    <rule>
        <from>^(.+)\.json$</from>
        <to type="forward">$1</to>
    </rule>
</urlrewrite>
  1. 在Spring Boot里注册这个Filter:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import org.springframework.core.Ordered;

@Configuration
public class UrlRewriteConfig {

    @Bean
    public FilterRegistrationBean<UrlRewriteFilter> urlRewriteFilter() {
        FilterRegistrationBean<UrlRewriteFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new UrlRewriteFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }
}

这种方式适合后续可能需要更多URL规则的场景,配置修改更方便。

方案3:内容协商配置(仅针对.json后缀)

如果.json后缀是用来做内容协商的,可以配置Spring MVC的内容协商管理器,让框架识别后缀并映射到对应媒体类型,同时不影响路径匹配:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(true)
                  .mediaType("json", org.springframework.http.MediaType.APPLICATION_JSON)
                  .ignoreAcceptHeader(false);
    }
}

注意:这个方案只处理.json后缀,.s后缀还是需要结合前两种方案处理。

方案对比

  • 自定义Filter:轻量无依赖,代码可控,适合仅需剥离特定后缀的简单场景。
  • UrlRewriteFilter:功能强大,规则配置灵活,适合未来有扩展需求的场景。
  • 内容协商配置:仅适配与媒体类型相关的后缀,局限性大。

内容的提问来源于stack exchange,提问作者Jakub Królikowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:20:08