首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring MVC RESTful 配置常見陷阱:靜態資源衝突、路徑匹配、內容協商完整解析

     編輯:Java 相關知識

RESTful 配置核心衝突點

Spring MVC 開啟 REST 風格(<mvc:annotation-driven>)後,常遇到靜態資源、favicon、錯誤頁面等請求被 DispatcherServlet 攔截導致 404/405/406 錯誤。

陷阱一:靜態資源被 Controller 攔截

現象

訪問 /css/style.css 進入 Controller 方法,報 405 Method Not Allowed 或 404。

原因

/ 或 /** 映射優先級高於靜態資源處理器。

解決方案

<!-- 1. 精確映射 Controller,避免 /** -->
@RequestMapping("/api/users")

<!-- 2. 配置靜態資源處理優先 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:resources mapping="/favicon.ico" location="/favicon.ico"/>

<!-- 3. 預設 Servlet 兜底 -->
<mvc:default-servlet-handler/>

陷阱二:路徑變量與靜態路徑衝突

現象

@GetMapping("/users/{id}") 與 @GetMapping("/users/profile") 衝突,profile 被當作 {id}。

解決方案

<!-- 1. 使用正則限制路徑變量 -->
@GetMapping("/users/{id:\\d+}")  // 僅匹配數字
@GetMapping("/users/profile")    // 精確路徑優先定義

<!-- 2. 或調整映射順序 -->
@GetMapping("/users/profile")
@GetMapping("/users/{id}")

陷阱三:內容協商與 Accept 標頭

現象

瀏覽器訪問 API 返回 406 Not Acceptable,或強制返回 HTML 而非 JSON。

原因

預設 ContentNegotiationManager 依據 Accept 標頭決定返回類型,瀏覽器預設 text/html。

解決方案

<mvc:annotation-driven>
  <mvc:message-converters>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>application/json;charset=UTF-8</value>
          <value>text/html;charset=UTF-8</value>  <!-- 支援瀏覽器直接訪問 -->
        </list>
      </property>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

<!-- 或全域配置預設內容類型 -->
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false)
                  .favorParameter(false)
                  .ignoreAcceptHeader(true)  // 忽略 Accept,預設 JSON
                  .defaultMediaType(MediaType.APPLICATION_JSON);
    }
}

陷阱四:PUT/DELETE 請求被過濾器攔截

現象

表單提交 PUT/DELETE 方法,Controller 收到 POST 或 405 錯誤。

解決方案

<!-- web.xml 配置 HiddenHttpMethodFilter(需在 Spring Security 之前)-->
<filter>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 前端表單使用 _method 參數 -->
<form method="POST" action="/api/users/1">
  <input type="hidden" name="_method" value="DELETE"/>
  <button type="submit">刪除</button>
</form>

陷阱五:路徑匹配尾部斜線問題

現象

/api/users 和 /api/users/ 導向不同處理器,或一個 404。

解決方案

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseTrailingSlashMatch(false); // 關閉尾部斜線自動匹配
    }
}

完整最佳實踐配置模板

@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/")
                .setCachePeriod(31536000)
                .resourceChain(true)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                .addTransformer(new GzipResourceTransformer());
        
        registry.addResourceHandler("/favicon.ico")
                .addResourceLocations("/favicon.ico");
    }
    
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable(); // 啟用容器 DefaultServlet
    }
    
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false)
                  .ignoreAcceptHeader(true)
                  .defaultMediaType(MediaType.APPLICATION_JSON);
    }
    
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseTrailingSlashMatch(false);
    }
    
    @Override
    public void extendMessageConverters(List> converters) {
        // 確保 JSON 轉換器支援 text/html
        converters.stream()
            .filter(c -> c instanceof MappingJackson2HttpMessageConverter)
            .findFirst()
            .ifPresent(c -> ((MappingJackson2HttpMessageConverter)c)
                .setSupportedMediaTypes(Arrays.asList(
                    MediaType.APPLICATION_JSON_UTF8,
                    MediaType.TEXT_HTML)));
    }
}

除錯技巧

  1. 啟用 DEBUG 日誌:logging.level.org.springframework.web=DEBUG
  2. 查看 HandlerMapping 匹配順序:/mappings Actuator 端點
  3. 使用 curl -v -H "Accept: application/json" URL 測試內容協商
  4. 檢查 Filter 鏈順序:FilterRegistrationBean.setOrder()
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved