Spring REST配置指南与问题总结

    技术2022-05-11  26

    下一版本的rapid-framework需要集成spring RESTful URL。最近JavaEye的badqiu对于如何搭建spring RESTful URL进行了研究,并总结问题如下。

    springmvc 3.0 中增加 RESTful URL功能,构造出类似javaeye现在的URL。比如如下URL

    /blog/ 1   HTTP GET =>    得到id =  1 的blog   /blog/ 1   HTTP DELETE => 删除 id =  1 的blog   /blog/ 1   HTTP PUT  =>   更新id =  1 的blog   /blog     HTTP POST =>   新增BLOG 

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

    首先,我们带着如下两个问题查看本文。

    1. 如何在java构造没有扩展名的RESTful url,如 /forms/1,而不是 /forms/1.do

    2. 浏览器的form标签不支持提交delete,put请求,如何曲线解决

    springmvc rest 实现

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

    @RequestMapping (value= "/blog/{id}" ,method=RequestMethod.DELETE)   public  ModelAndView delete( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response) {    blogManager.removeById(id);     return   new  ModelAndView(LIST_ACTION);   }     

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

    @RequestMapping (value= "/blog/{blogId}/message/{msgId}" ,method=RequestMethod.DELETE)   public  ModelAndView delete( @PathVariable ( "blogId" ) Long blogId, @PathVariable ( "msgId" ) Long msgId,HttpServletRequest request,HttpServletResponse response) {   }     

    spring rest配置指南

    1. springmvc web.xml配置

    <  !-- 该servlet为tomcat,jetty等容器提供,将静态资源映射从/改为/static/目录,如原来访问 http://localhost/foo.css ,现在http://localhost/static/foo.css -- >   <   servlet-mapping >     <   servlet-name > default <  /servlet-name >     <   url-pattern > /static/* <  /url-pattern >   <  /servlet-mapping >   <   servlet >        <   servlet-name > springmvc <  /servlet-name >        <   servlet-class > org.springframework.web.servlet.DispatcherServlet <  /servlet-class >        <   load-on-startup > 1 <  /load-on-startup >   <  /servlet >     <  !-- URL重写filter,用于将访问静态资源http://localhost/foo.css 转为http://localhost/static/foo.css -- >   <   filter >     <   filter-name > UrlRewriteFilter <  /filter-name >     <   filter-class > org.tuckey.web.filters.urlrewrite.UrlRewriteFilter <  /filter-class >     <   init-param >          <   param-name > confReloadCheckInterval <  /param-name >          <   param-value > 60 <  /param-value >         <  /init-param >     <   init-param >                 <   param-name > logLevel <  /param-name >                 <   param-value > DEBUG <  /param-value >            <  /init-param >         <  /filter >   <   filter-mapping >     <   filter-name > UrlRewriteFilter <  /filter-name >     <   url-pattern > /* <  /url-pattern >   <  /filter-mapping >     <  !-- 覆盖default servlet的/, springmvc servlet将处理原来处理静态资源的映射 -- >   <   servlet-mapping >        <   servlet-name > springmvc <  /servlet-name >        <   url-pattern > / <  /url-pattern >   <  /servlet-mapping >     <  !-- 浏览器不支持put,delete等method,由该filter将/blog? _method = delete 转换为标准的http delete方法 -- >   <   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 >  

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

    <   bean   class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />   <   bean   class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />  

    完整配置

    <   beans   default-autowire = "byName"     >       <  !-- 自动搜索@Controller标注的类 -- >     <   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" />          <  !-- Default ViewResolver -- >        <   bean   id = "viewResolver"   class = "org.springframework.web.servlet.view.InternalResourceViewResolver" >            <   property   name = "viewClass"   value = "org.springframework.web.servlet.view.JstlView" />            <   property   name = "prefix"   value = "/pages" />            <   property   name = "suffix"   value = ".jsp" > <  /property >        <  /bean >               <   bean   id = "messageSource"   class = "org.springframework.context.support.ResourceBundleMessageSource"   p:basename = "i18n/messages" />          <  !-- Mapping exception to the handler view -- >        <   bean   id = "exceptionResolver"   class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >         <  !-- to /commons/error.jsp -- >            <   property   name = "defaultErrorView"   value = "/commons/error" />            <   property   name = "exceptionMappings" >                <   props >                <  /props >            <  /property >        <  /bean >              <  /beans >      

    3. Controller编写

    /**    * @RequestMapping("/userinfo") 具有层次关系,方法级的将在类一级@RequestMapping之一,    * 如下面示例, 访问方法级别的@RequestMapping("/new"),则URL为 /userinfo/new    */   @Controller   @RequestMapping ( "/userinfo" )   public   class  UserInfoController  extends  BaseSpringController{     //默认多列排序,example: username desc,createTime asc     protected   static   final  String DEFAULT_SORT_COLUMNS =  null ;          private  UserInfoManager userInfoManager;         private   final  String LIST_ACTION =  "redirect:/userinfo" ;         /**     * 通过spring自动注入     **/     public   void  setUserInfoManager(UserInfoManager manager) {      this .userInfoManager = manager;    }         /** 列表 */     @RequestMapping     public  ModelAndView index(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo) {     PageRequest< Map> pageRequest = newPageRequest(request,DEFAULT_SORT_COLUMNS);      //pageRequest.getFilters(); //add custom filters          Page page =  this .userInfoManager.findByPageRequest(pageRequest);     savePage(page,pageRequest,request);      return   new  ModelAndView( "/userinfo/list" , "userInfo" ,userInfo);    }         /** 进入新增 */     @RequestMapping (value= "/new" )     public  ModelAndView _new(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo)  throws  Exception {      return   new  ModelAndView( "/userinfo/new" , "userInfo" ,userInfo);    }         /** 显示 */     @RequestMapping (value= "/{id}" )     public  ModelAndView show( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {     UserInfo userInfo = (UserInfo)userInfoManager.getById(id);      return   new  ModelAndView( "/userinfo/show" , "userInfo" ,userInfo);    }         /** 编辑 */     @RequestMapping (value= "/{id}/edit" )     public  ModelAndView edit( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {     UserInfo userInfo = (UserInfo)userInfoManager.getById(id);      return   new  ModelAndView( "/userinfo/edit" , "userInfo" ,userInfo);    }         /** 保存新增 */     @RequestMapping (method=RequestMethod.POST)     public  ModelAndView create(HttpServletRequest request,HttpServletResponse response,UserInfo userInfo)  throws  Exception {     userInfoManager.save(userInfo);      return   new  ModelAndView(LIST_ACTION);    }         /** 保存更新 */     @RequestMapping (value= "/{id}" ,method=RequestMethod.PUT)     public  ModelAndView update( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response)  throws  Exception {     UserInfo userInfo = (UserInfo)userInfoManager.getById(id);     bind(request,userInfo);     userInfoManager.update(userInfo);      return   new  ModelAndView(LIST_ACTION);    }         /** 删除 */     @RequestMapping (value= "/{id}" ,method=RequestMethod.DELETE)     public  ModelAndView delete( @PathVariable  Long id,HttpServletRequest request,HttpServletResponse response) {     userInfoManager.removeById(id);      return   new  ModelAndView(LIST_ACTION);    }       /** 批量删除 */     @RequestMapping (method=RequestMethod.DELETE)     public  ModelAndView batchDelete(HttpServletRequest request,HttpServletResponse response) {     String[] items = request.getParameterValues( "items" );      for ( int  i =  0 ; i <  items.length; i++) {      java.lang.Long id =  new  java.lang.Long(items[i]);      userInfoManager.removeById(id);     }      return   new  ModelAndView(LIST_ACTION);    }       }      

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

    /userinfo    => index()   /userinfo/new  => _new()   /userinfo/{id}  =>  show ()   /userinfo/{id}/edit   => edit()   /userinfo  POST  => create()   /userinfo/{id}  PUT => update()   /userinfo/{id}  DELETE => delete()   /userinfo  DELETE  => batchDelete()  

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

    4. jsp 编写

    <   form:form   action = "${ctx}/userinfo/${userInfo.userId}"   method = "put" >   <  /form:form >    将生成一个hidden的 _method = put ,并于web.xml中的HiddenHttpMethodFilter配合使用,在服务端将post请求改为put请求     <   form   id = "userInfo"   action = "/springmvc_rest_demo/userinfo/2"   method = "post" >     <   input   type = "hidden"   name = "_method"   value = "put" />   <  /form >      

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

    5. 静态资源的URL重写

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

    如 /foo.gif, 现在访问该文件将是 /static/foo.gif.

    那如何避免这个前缀呢,那就是应用URL rewrite,现我们使用 http://tuckey.org/urlrewrite/ , 重写规则如下

    <   urlrewrite >        <  !-- 访问jsp及jspx将不rewrite url,其它.js,.css,.gif等将重写,如 / foo.gif  = >  /static/foo.gif -- >        <   rule >         <   condition   operator = "notequal"   next = "and"   type = "request-uri" > .*.jsp <  /condition >         <   condition   operator = "notequal"   next = "and"   type = "request-uri" > .*.jspx <  /condition >            <   from > ^(/.*/..*)$ <  /from >            <   to > /static$1 <  /to >        <  /rule >   <  /urlrewrite >       

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

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

     

    http://developer.51cto.com/art/200909/153054.htm

     

     

     

     


    最新回复(0)