Spring 3.0 引入原生 REST 支援,結合 @RequestMapping、@PathVariable、HiddenHttpMethodFilter,可構建乾淨無副檔名的 RESTful API。本文從 web.xml 配置到 Controller、JSP、靜態資源重寫,全鏈路實戰落地。
| HTTP Method | URL | 語義 | Controller 方法 |
|---|---|---|---|
| GET | /blog/1 | 獲取 ID=1 部落格 | show() |
| DELETE | /blog/1 | 刪除 ID=1 部落格 | delete() |
| PUT | /blog/1 | 更新 ID=1 部落格 | update() |
| POST | /blog | 新增部落格 | create() |
<!-- 1. 靜態資源交給 default servlet,映射到 /static/ -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
<!-- 2. Spring MVC 處理所有請求(含原靜態資源路徑) -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 3. URL Rewrite:將 /foo.css → /static/foo.css -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 4. 瀏覽器不支援 PUT/DELETE,透過 _method 參數覆蓋 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<context:component-scan base-package="com.**.controller"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages"/>
<property name="suffix" value=".jsp"/>
</bean>
@Controller
@RequestMapping("/userinfo")
public class UserInfoController {
private UserInfoManager userInfoManager;
@RequestMapping(method = RequestMethod.GET) // /userinfo
public ModelAndView index(...) { ... }
@RequestMapping(value = "/new", method = RequestMethod.GET) // /userinfo/new
public ModelAndView _new(...) { ... }
@RequestMapping(value = "/{id}", method = RequestMethod.GET) // /userinfo/1
public ModelAndView show(@PathVariable Long id, ...) { ... }
@RequestMapping(value = "/{id}/edit", method = RequestMethod.GET) // /userinfo/1/edit
public ModelAndView edit(@PathVariable Long id, ...) { ... }
@RequestMapping(method = RequestMethod.POST) // /userinfo
public ModelAndView create(...) { ... }
@RequestMapping(value = "/{id}", method = RequestMethod.PUT) // /userinfo/1
public ModelAndView update(@PathVariable Long id, ...) { ... }
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE) // /userinfo/1
public ModelAndView delete(@PathVariable Long id, ...) { ... }
@RequestMapping(method = RequestMethod.DELETE) // /userinfo?items=1,2,3
public ModelAndView batchDelete(...) { ... }
}
<form:form action="${ctx}/userinfo/${userInfo.userId}" method="put">
</form:form>
生成 HTML 自動包含隱藏欄位:
<form action="/springmvc_rest_demo/userinfo/2" method="post">
<input type="hidden" name="_method" value="put"/>
</form>
HiddenHttpMethodFilter 攔截 _method 參數,將 POST 轉為真實 PUT/DELETE。
<urlrewrite>
<rule>
<condition type="request-uri" operator="notequal">.*.jsp</condition>
<condition type="request-uri" operator="notequal">.*.jspx</condition>
<from>^(/.*\..*)$</from>
<to>/static$1</to>
</rule>
</urlrewrite>
訪問 /foo.css 自動重寫為 /static/foo.css,JSP 請求直通不重寫。
遵循此架構,即可擁有乾淨、語義化、前後端分離友好的 RESTful API 體系。