spring 3.0 应用springmvc 构造RESTful URL 详

系统 1547 0

在线springmvc_rest demo

 

 

由于下一版本的 rapid-framwork 需要集成spring RESTful URL,所以研究了一下怎么搭建. 并碰到了一下问题。

 

springmvc 3.0 中增加 RESTful URL功能,构造出类似javaeye现在的URL。 rest介绍  , 这里还有struts2 rest构造的一篇文章:  使用 Struts 2 开发 RESTful 服务

简单例子如下,比如如下URL

Java代码   收藏代码
  1. /blog/ 1   HTTP GET =>    得到id =  1 的blog  
  2. /blog/ 1   HTTP DELETE => 删除 id =  1 的blog  
  3. /blog/ 1   HTTP PUT  =>   更新id =  1 的blog  
  4. /blog     HTTP POST =>   新增BLOG  

 

 

以下详细解一下spring rest使用.

 

首先,我们带着如下 个问题 查看本文。
1. 如何在java构造没有扩展名的RESTful url,如 /forms/1,而不是 /forms/1.do

2. 由于我们要构造没有扩展名的url本来是处理静态资源的容器映射的,现在被我们的spring占用了,冲突怎么解决?
3. 浏览器的form标签不支持提交delete,put请求,如何曲线解决?

 

springmvc rest 实现


springmvc的resturl是通过@RequestMapping 及@PathVariable annotation提供的,通过如@RequestMapping(value="/blog /{id}",method=RequestMethod.DELETE)即可处理/blog/1 的delete请求.

Java代码   收藏代码
  1. @RequestMapping (value= "/blog/{id}" ,method=RequestMethod.DELETE)  
  2. public  ModelAndView delete( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response) {  
  3.     blogManager.removeById(id);  
  4.      return   new  ModelAndView(LIST_ACTION);  
  5. }  

 

@RequestMapping @PathVariable如果URL中带参数,则配合使用,如

Java代码   收藏代码
  1. @RequestMapping (value= "/blog/{blogId}/message/{msgId}" ,method=RequestMethod.DELETE)  
  2. public  ModelAndView delete( @PathVariable ( "blogId" ) Long blogId, @PathVariable ( "msgId" ) Long msgId,HttpServletRequest request,HttpServletResponse response) {  
  3. }  

 

 spring rest配置指南

1. springmvc web.xml配置

Xml代码   收藏代码
  1. <!-- 该 servlet为tomcat,jetty等容器提供,将静态资源映射从/改为/static/目录,如原来访问 http://localhost /foo.css ,现在http://localhost/static/foo.css -->   
  2. < servlet-mapping >   
  3.      < servlet-name > default </ servlet-name >   
  4.      < url-pattern > /static/* </ url-pattern >   
  5. </ servlet-mapping >   
  6. < servlet >   
  7.      < servlet-name > springmvc </ servlet-name >   
  8.      < servlet-class > org.springframework.web.servlet.DispatcherServlet </ servlet-class >   
  9.      < load-on-startup > 1 </ load-on-startup >   
  10. </ servlet >   
  11.   
  12. <!-- URL重写filter,用于将访问静态资源http://localhost/foo.css 转为http://localhost/static/foo.css -->   
  13. < filter >   
  14.      < filter-name > UrlRewriteFilter </ filter-name >   
  15.      < filter-class > org.tuckey.web.filters.urlrewrite.UrlRewriteFilter </ filter-class >   
  16.      < init-param >   
  17.              < param-name > confReloadCheckInterval </ param-name >   
  18.              < param-value > 60 </ param-value >   
  19.          </ init-param >   
  20.      < init-param >   
  21.                  < param-name > logLevel </ param-name >   
  22.                  < param-value > DEBUG </ param-value >   
  23.          </ init-param >        
  24. </ filter >   
  25. < filter-mapping >   
  26.      < filter-name > UrlRewriteFilter </ filter-name >   
  27.      < url-pattern > /* </ url-pattern >   
  28. </ filter-mapping >   
  29.   
  30. <!-- 覆盖default servlet的/, springmvc servlet将处理原来处理静态资源的映射 -->   
  31. < servlet-mapping >   
  32.      < servlet-name > springmvc </ servlet-name >   
  33.      < url-pattern > / </ url-pattern >   
  34. </ servlet-mapping >   
  35.   
  36. <!-- 浏览器不支持put,delete等method,由该filter将/blog?_method=delete转换为标准的http delete方法 -->   
  37. < filter >   
  38.      < filter-name > HiddenHttpMethodFilter </ filter-name >   
  39.      < filter-class > org.springframework.web.filter.HiddenHttpMethodFilter </ filter-class >   
  40. </ filter >   
  41.   
  42. < filter-mapping >   
  43.      < filter-name > HiddenHttpMethodFilter </ filter-name >   
  44.      < servlet-name > springmvc </ servlet-name >   
  45. </ filter-mapping >   

 

 

2. webapp/WEB-INF/springmvc-servlet.xml配置,使用如下两个class激活@RequestMapping annotation

Java代码   收藏代码
  1. <bean  class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />  
  2. <bean  class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />  

 

完整配置

Java代码   收藏代码
  1. <beans  default -autowire= "byName"    >  
  2.   
  3.     <!-- 自动搜索 @Controller 标注的类 -->  
  4.     <context:component-scan base- package = "com.**.controller" />  
  5.       
  6.     <bean  class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />  
  7.   
  8.     <bean  class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />  
  9.   
  10.     <!-- Default ViewResolver -->  
  11.     <bean id= "viewResolver"   class = "org.springframework.web.servlet.view.InternalResourceViewResolver" >  
  12.         <property name= "viewClass"  value= "org.springframework.web.servlet.view.JstlView" />  
  13.         <property name= "prefix"  value= "/pages" />  
  14.         <property name= "suffix"  value= ".jsp" ></property>  
  15.     </bean>  
  16.       
  17.     <bean id= "messageSource"   class = "org.springframework.context.support.ResourceBundleMessageSource"  p:basename= "i18n/messages" />  
  18.   
  19.     <!-- Mapping exception to the handler view -->  
  20.     <bean id= "exceptionResolver"   class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >  
  21.         <!-- to /commons/error.jsp -->  
  22.         <property name= "defaultErrorView"  value= "/commons/error" />  
  23.         <property name= "exceptionMappings" >  
  24.             <props>  
  25.             </props>  
  26.         </property>  
  27.     </bean>  
  28.           
  29. </beans>  

 

 

3. Controller编写

Java代码   收藏代码
  1. /**  
  2.  * @RequestMapping("/userinfo") 具有层次关系,方法级的将在类一级@RequestMapping之一,  
  3.  * 如下面示例, 访问方法级别的@RequestMapping("/new"),则URL为 /userinfo/new  
  4.  */   
  5. @Controller   
  6. @RequestMapping ( "/userinfo" )  
  7. public   class  UserInfoController  extends  BaseSpringController{  
  8.      //默认多列排序,example: username desc,createTime asc   
  9.      protected   static   final  String DEFAULT_SORT_COLUMNS =  null ;   
  10.       
  11.      private  UserInfoManager userInfoManager;  
  12.       
  13.      private   final  String LIST_ACTION =  "redirect:/userinfo" ;  
  14.       
  15.      /**   
  16.      * 通过spring自动注入  
  17.      **/   
  18.      public   void  setUserInfoManager(UserInfoManager manager) {  
  19.          this .userInfoManager = manager;  
  20.     }  
  21.       
  22.      /** 列表 */   
  23.      @RequestMapping   
  24.      public  ModelAndView index(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo) {  
  25.         PageRequest<Map> pageRequest = newPageRequest(request,DEFAULT_SORT_COLUMNS);  
  26.          //pageRequest.getFilters(); //add custom filters   
  27.           
  28.         Page page =  this .userInfoManager.findByPageRequest(pageRequest);  
  29.         savePage(page,pageRequest,request);  
  30.          return   new  ModelAndView( "/userinfo/list" , "userInfo" ,userInfo);  
  31.     }  
  32.       
  33.      /** 进入新增 */   
  34.      @RequestMapping (value= "/new" )  
  35.      public  ModelAndView _new(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo)  throws  Exception {  
  36.          return   new  ModelAndView( "/userinfo/new" , "userInfo" ,userInfo);  
  37.     }  
  38.       
  39.      /** 显示 */   
  40.      @RequestMapping (value= "/{id}" )  
  41.      public  ModelAndView show( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {  
  42.         UserInfo userInfo = (UserInfo)userInfoManager.getById(id);  
  43.          return   new  ModelAndView( "/userinfo/show" , "userInfo" ,userInfo);  
  44.     }  
  45.       
  46.      /** 编辑 */   
  47.      @RequestMapping (value= "/{id}/edit" )  
  48.      public  ModelAndView edit( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {  
  49.         UserInfo userInfo = (UserInfo)userInfoManager.getById(id);  
  50.          return   new  ModelAndView( "/userinfo/edit" , "userInfo" ,userInfo);  
  51.     }  
  52.       
  53.      /** 保存新增 */   
  54.      @RequestMapping (method=RequestMethod.POST)  
  55.      public  ModelAndView create(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo)  throws  Exception {  
  56.         userInfoManager.save(userInfo);  
  57.          return   new  ModelAndView(LIST_ACTION);  
  58.     }  
  59.       
  60.      /** 保存更新 */   
  61.      @RequestMapping (value= "/{id}" ,method=RequestMethod.PUT)  
  62.      public  ModelAndView update( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {  
  63.         UserInfo userInfo = (UserInfo)userInfoManager.getById(id);  
  64.         bind(request,userInfo);  
  65.         userInfoManager.update(userInfo);  
  66.          return   new  ModelAndView(LIST_ACTION);  
  67.     }  
  68.       
  69.      /** 删除 */   
  70.      @RequestMapping (value= "/{id}" ,method=RequestMethod.DELETE)  
  71.      public  ModelAndView delete( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response) {  
  72.         userInfoManager.removeById(id);  
  73.          return   new  ModelAndView(LIST_ACTION);  
  74.     }  
  75.   
  76.      /** 批量删除 */   
  77.      @RequestMapping (method=RequestMethod.DELETE)  
  78.      public  ModelAndView batchDelete( @RequestParam ( "items" ) Long[] items,HttpServletRequest request,HttpServletResponse response) {  
  79.           
  80.          for ( int  i =  0 ; i < items.length; i++) {  
  81.               
  82.             userInfoManager.removeById(items[i]);  
  83.         }  
  84.          return   new  ModelAndView(LIST_ACTION);  
  85.     }  
  86.       
  87. }  

 

 

上面是 rapid-framework 新版本生成器生成的代码,以后也将应用此规则,rest url中增删改查等基本方法与Controller的方法映射规则

Java代码   收藏代码
  1. /userinfo           => index()  
  2. /userinfo/ new        => _new()  
  3. /userinfo/{id}      => show()  
  4. /userinfo/{id}/edit         => edit()  
  5. /userinfo   POST        => create()  
  6. /userinfo/{id}  PUT => update()  
  7. /userinfo/{id}  DELETE  => delete()  
  8. /userinfo   DELETE      => batchDelete()  

 注(不使用 /userinfo/add  => add() 方法是由于add这个方法会被maxthon浏览器当做广告链接过滤掉,因为包含ad字符)

 

4. jsp 编写

Html代码   收藏代码
  1. < form:form   action = "${ctx}/userinfo/${userInfo.userId}"   method = "put" >   
  2. </ form:form >   

 生成的html内容如下, 生成一个hidden的_method=put,并于web.xml中的HiddenHttpMethodFilter配合使用,在服务端将post请求改为put请求

Java代码   收藏代码
  1. <form id= "userInfo"  action= "/springmvc_rest_demo/userinfo/2"  method= "post" >  
  2.     <input type= "hidden"  name= "_method"  value= "put" />  
  3. </form>  

 

另外一种方法是你可以使用ajax发送put,delete请求.

 

5. 静态资源的URL重写

   如上我们描述,现因为将default servlet映射至/static/的子目录,现我们访问静态资源将会带一个/static/前缀.

   如 /foo.gif, 现在访问该文件将是 /static/foo.gif.
   那如何避免这个前缀呢,那就是应用URL rewrite,现我们使用 http://tuckey.org/urlrewrite/ , 重写规则如下

 

Xml代码   收藏代码
  1. < urlrewrite >   
  2.      <!-- 访问jsp及jspx将不rewrite url,其它.js,.css,.gif等将重写,如 /foo.gif => /static/foo.gif -->   
  3.      < rule >   
  4.          < condition   operator = "notequal"   next = "and"   type = "request-uri" > .*.jsp </ condition >   
  5.          < condition   operator = "notequal"   next = "and"   type = "request-uri" > .*.jspx </ condition >   
  6.          < from > ^(/.*\..*)$ </ from >   
  7.          < to > /static$1 </ to >   
  8.      </ rule >   
  9. </ urlrewrite >   

   另笔者专门写了一个 RestUrlRewriteFilter来做同样的事件,以后会随着 rapid-framework 一起发布. 比这个更加轻量级.

 

并且 该代码 已经贡献给spring,不知会不会在下一版本发布

 

 

在线DEMO地址 http://demo.rapid-framework.org.cn:8080/springmvc_rest_demo/userinfo

spring 3.0 应用springmvc 构造RESTful URL 详细讲解


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论