[转]spring 3.0 应用springmvc 构造RESTful URL 详细讲解(一)

阅读更多
只有对应的HandlerMapping(为了实现类型级别的注解)和/或HandlerAdapter(为了实现方法级别的注解)出现在dispatcher中时, @RequestMapping才会被处理。 这在DispatcherServlet和DispatcherPortlet中都是缺省的行为。

然而,如果是在定义自己的HandlerMappings或HandlerAdapters, 就需要确保一个对应的自定义的DefaultAnnotationHandlerMapping和/或AnnotationMethodHandlerAdapter同样被定义——假设想要使用@RequestMapping。

    

    



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


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

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

Java代码
/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  

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


springmvc rest 实现

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

Java代码
@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(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中带参数,则配合使用,如

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

@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配置

Xml代码
  
  
    default  
    /static/*  
  
  
    springmvc  
    org.springframework.web.servlet.DispatcherServlet  
    1  
  
  
  
  
    UrlRewriteFilter  
    org.tuckey.web.filters.urlrewrite.UrlRewriteFilter  
      
            confReloadCheckInterval  
            60  
       
  
      
                logLevel  
                DEBUG  
       
        
  
  
    UrlRewriteFilter  
    /*  
  
  
  
  
    springmvc  
    /  
  
  
  
  
    HiddenHttpMethodFilter  
    org.springframework.web.filter.HiddenHttpMethodFilter  
  
  
  
    HiddenHttpMethodFilter  
    springmvc  
  



  default
  /static/*


     springmvc
     org.springframework.web.servlet.DispatcherServlet
     1




  UrlRewriteFilter
  org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
 
       confReloadCheckInterval
       60
     

 
              logLevel
              DEBUG
        
    


  UrlRewriteFilter
  /*




     springmvc
     /




  HiddenHttpMethodFilter
  org.springframework.web.filter.HiddenHttpMethodFilter



  HiddenHttpMethodFilter
  springmvc



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

Java代码
   
  


   

完整配置

Java代码
   
  
        
        
       
        
  
        
  
        
        
            
            
            
    
   
       
        
  
        
        
            
            
            
                
            
   
        
   
    
   
           
  

3. Controller编写

Java代码
/**
* @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 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(@RequestParam("items") Long[] items,HttpServletRequest request,HttpServletResponse response) {   
           
        for(int i = 0; i < items.length; i++) {   
               
             userInfoManager.removeById(items[i]);   
         }   
        return new ModelAndView(LIST_ACTION);   
     }   
       
}  

/**
* @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 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(@RequestParam("items") Long[] items,HttpServletRequest request,HttpServletResponse response) {
 
  for(int i = 0; i < items.length; i++) {
  
   userInfoManager.removeById(items[i]);
  }
  return new ModelAndView(LIST_ACTION);
}

}


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

Java代码
/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    => 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 编写

Html代码
  
  



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

Java代码
   
        
  





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


5. 静态资源的URL重写

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

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


Xml代码
  
      
      
        .*.jsp  
        .*.jspx  
        ^(/.*\..*)$  
        /static$1  
   
  
  


   
   
     .*.jsp
     .*.jspx
        ^(/.*\..*)$
        /static$1
   


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

-------------------------------------------------
自己的一些应用如下:

1、rest-servlet.xml文件

		
			
			
				
					
				
				
		
		
		
		

		
		
			
		


2、controller-servlet.xml

		
		
		
		
	


3、AppPageController.java
/**
 * $Id$
 * Copyright 2009-2010 Oak Pacific Interactive. All rights reserved.
 */
package com.renren.wap.wqo.controllers;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;

import com.metaparadigm.jsonrpc.HttpServletRequestArgResolver;
import com.renren.wap.fuxi.constants.FuxiConstants;
import com.renren.wap.fuxi.controllers.AppPageController;
import com.xiaonei.platform.core.model.User;
import com.xiaonei.wap.framework.user.facade.IUserFacade;
import com.xiaonei.wap.fuxi.constant.AppPageConstant;
import com.xiaonei.wap.fuxi.model.AppPageResult;
import com.xiaonei.wap.fuxi.model.AppPageSnapshot;
import com.xiaonei.wap.fuxi.model.AppPageSupport;
import com.xiaonei.wap.fuxi.service.IAppPageCategoryService;
import com.xiaonei.wap.fuxi.service.IAppPageService;
import com.xiaonei.wap.fuxi.service.IUserPlayService;


//--------------------- Change Logs----------------------
// 

@author wangqiao Initial Created at 2011-8-9

//------------------------------------------------------- // // //@Controller注解,添加了@Controller注解注解的类就可以担任控制器(Action)的职责 @Controller //* @RequestMapping("/page") 具有层次关系,方法级的将在类一级@RequestMapping之一, //* 如下面示例, 访问方法级别的@RequestMapping("/{pageId}"),则URL为 /page/1 @RequestMapping("/page") public class AppPageController { /** * Logger for this class */ private static final Log logger = LogFactory.getLog(AppPageController.class); private IAppPageService appPageService; private IAppPageCategoryService appPageCategoryService; private IUserPlayService userPlayService; private IUserFacade userFacade; //springmvc的resturl是通过@RequestMapping 及@PathVariable annotation提供的, //通过如@RequestMapping(value="/blog/{id}",method=RequestMethod.DELETE)即可处理/blog/1 的delete请求 @RequestMapping(value = "/{pageId}", method = RequestMethod.GET) //@RequestMapping @PathVariable如果URL中带参数,则配合使用 //springmvc提供了@RequestParam注释帮助我们获取参数。 //用法@RequestParam("接收的参数名") public ModelAndView page( HttpServletRequest request, User host, @PathVariable("pageId") int pageId, @RequestParam(value = "os", required = false, defaultValue = FuxiConstants.OS_VALUE) int os) throws NoSuchRequestHandlingMethodException { ModelAndView mav = new ModelAndView("pages/wap/apppage"); AppPage appPage = appPageService.getPage(pageId); if (appPage == null) { throw new NoSuchRequestHandlingMethodException(request); } String downloadUrl = null; float price = 0; int priceUnit = 1; if (appPage.getType() == AppPageConstant.APP_PAGE_TYPE_DOWNLOAD) { List appPageSupportList = appPage.getAppPageSupports(); if (!CollectionUtils.isEmpty(appPageSupportList)) { for (AppPageSupport appPageSupport : appPageSupportList) { if (os == appPageSupport.getOs()) { downloadUrl = appPageSupport.getWapDownloadUrl(); price = appPageSupport.getPrice(); priceUnit = appPageSupport.getPriceUnit(); break; } } } } else { downloadUrl = appPage.getWebDownloadUrl(); } mav.addObject("price", price); mav.addObject("priceUnit", priceUnit); if (downloadUrl != null) { mav.addObject("downloadUrl", downloadUrl); } List snapshot = null; if (appPage.getWapSnapshots() != null) { snapshot = appPage.getWapSnapshots(); } mav.addObject("snapshot", snapshot); List friendIdList = userPlayService.getFriendPlayByPageId(host.getId(), pageId, FuxiConstants.EXPIRED_DAYS); if (!CollectionUtils.isEmpty(friendIdList)) { String friends = getFriendsNameAndCount(friendIdList); appPage.setFriends(friends); } mav.addObject("appPage", appPage); return mav; } @RequestMapping(value = "/hot/list", method = RequestMethod.GET) public ModelAndView hotPages( HttpServletRequest request, User host, @RequestParam(value = "os", required = false, defaultValue = FuxiConstants.OS_VALUE) int os){ ModelAndView mav = new ModelAndView("pages/wap/apppageshot"); AppPageResult topList = appPageService.getTopPageList(os, 0, FuxiConstants.HOT_SIZE_VALUE); if (topList != null) { mav.addObject("topList", topList); } AppPageResult gameList = getCategory(os, FuxiConstants.APP_PAGE_CATEGORY_GAME); if (gameList != null) { buildAppPageList(host.getId(), gameList); mav.addObject("gameList", gameList); } AppPageResult appList = getCategory(os, FuxiConstants.APP_PAGE_CATEGORY_APP); if (appList != null) { buildAppPageList(host.getId(), appList); mav.addObject("appList", appList); } AppPageResult webList = getCategory(os, FuxiConstants.APP_PAGE_CATEGORY_WEB); if (webList != null) { buildAppPageList(host.getId(), webList); mav.addObject("webList", webList); } mav.addObject("os", os); return mav; } @RequestMapping(value = "/category/{categoryId}", method = RequestMethod.GET) public ModelAndView category( HttpServletRequest request, User host, @PathVariable("categoryId") int categoryId, @RequestParam(value = "os", required = false, defaultValue = FuxiConstants.OS_VALUE) int os, @RequestParam(value = "page", required = false, defaultValue = FuxiConstants.PAGE_VALUE) int page, @RequestParam(value = "pageSize", required = false, defaultValue = FuxiConstants.PAGE_SIZE_VALUE) int pageSize) { ModelAndView mav = new ModelAndView("pages/wap/apppagescategory"); AppPageResult appPageResult = appPageCategoryService.getCategoryPageList(categoryId, os, FuxiConstants.APP_PAGE_SORT_TYPE_DISPLAY, page, pageSize); if (appPageResult == null) { if (logger.isDebugEnabled()) { logger.debug("AppPageResult is null"); } return null; } buildAppPageList(host.getId(), appPageResult); mav.addObject(FuxiConstants.LIST, appPageResult); mav.addObject("categoryId", categoryId); mav.addObject("os", os); mav.addObject(FuxiConstants.PAGE, page); mav.addObject(FuxiConstants.COUNT, appPageResult.getCount()); mav.addObject(FuxiConstants.PAGE_SIZE, pageSize); return mav; } public void setAppPageService(IAppPageService appPageService) { this.appPageService = appPageService; } public void setAppPageCategoryService(IAppPageCategoryService appPageCategoryService) { this.appPageCategoryService = appPageCategoryService; } public void setUserPlayService(IUserPlayService userPlayService) { this.userPlayService = userPlayService; } public void setUserFacade(IUserFacade userFacade) { this.userFacade = userFacade; } }





你可能感兴趣的:([转]spring 3.0 应用springmvc 构造RESTful URL 详细讲解(一))