本文由 黑壳博客 转载
本文来源 什么是REST以及 RESTful??
imagepng imagepng自从Roy Fielding博士在2000年他的博士论文中提出 REST (Representational State Transfer)风格的软件架构模式后,REST就基本上迅速取代了复杂而笨重的SOAP,成为Web API的标准了。
什么是Web API呢?
如果我们想要获取某个电商网站的某个商品,输入 http://localhost:9999/products/123 ,就可以看到id为123的商品页面,但这个结果是HTML页面,它同时混合包含了Product的数据和Product的展示两个部分。对于用户来说,阅读起来没有问题,但是,如果机器读取,就很难从HTML中解析出Product的数据。
如果一个URL返回的不是HTML,而是机器能直接解析的数据,这个URL就可以看成是一个Web API。比如,读取 http://localhost:9999/api/products/123 ,如果能直接返回Product的数据,那么机器就可以直接读取。
REST就是一种设计API的模式。最常用的数据格式是JSON。由于JSON能直接被JavaScript读取,所以,以JSON格式编写的REST风格的API具有简单、易读、易用的特点。
编写API有什么好处呢?由于API就是把Web App的功能全部封装了,所以,通过API操作数据,可以极大地把前端和后端的代码隔离,使得后端代码易于测试,前端代码编写更简单。
此外,如果我们把前端页面看作是一种用于展示的客户端,那么API就是为客户端提供数据、操作数据的接口。这种设计可以获得极高的扩展性。例如,当用户需要在手机上购买商品时,只需要开发针对iOS和Android的两个客户端,通过客户端访问API,就可以完成通过浏览器页面提供的功能,而后端代码基本无需改动。
当一个Web应用以API的形式对外提供功能时,整个应用的结构就扩展为:
REST-arch REST-arch把网页视为一种客户端,是REST架构可扩展的一个关键。
** 网络上的所有事物都被抽象为资源**
**每个资源都有一个唯一的资源标识符**
**同一个资源具有多种表现形式(xml,json等)**
**对资源的各种操作不会改变资源标识符**
**所有的操作都是无状态的**
**符合REST原则的架构方式即可称为RESTful**
REST主要对以下两方面进行了规范
-定位资源的URL风格,例如
http://bhusk.com/admin/1234
http://bhusk.com/admin/1234/10/11
-如何对资源操作
采用HTTP协议规定的GET、POST、PUT、DELETE动作处理资源的增删该查操作
image image对应的中文是rest式的Restful web service是一种常见的rest的应用,是遵守了rest风格的web服务rest式的web服务是一种ROA(The Resource-Oriented Architecture)(面向资源的架构).
符合REST约束风格和原则的应用程序或设计就是RESTful.
- Spring MVC 对 RESTful应用提供了以下支持
- 利用@RequestMapping 指定要处理请求的URI模板和HTTP请求的动作类型
- 利用@PathVariable讲URI请求模板中的变量映射到处理方法参数上
- 利用Ajax,在客户端发出PUT、DELETE动作的请求
** 可以采用Ajax方式发送PUT和DELETE请求**
采用RESTful架构后,需要将web.xml中控制器拦截的请求设置为/,这样会将css,js等静态资源进行拦截,发送404错误。
解决方法:
本篇文章参考了一部分 http://t.cn/RuZZUK3 , http://t.cn/RKO0YPr
程序员是个辛苦的职业
请善待你们身边的每一位程序员~
欢迎在评论写下你的程序员趣事,程序员不是一个死板的职业~~
欢迎扫描二维码加入我们的小组织 ,大家都叫我壳叔,很期待你的到来。
黑壳网交流群 Q 群: 200408242
11a84075a304ac57f6d37323512fd24cde9836350b9d80148b282eeaa188b196c2358d4ffd7006cbpng 11a84075a304ac57f6d37323512fd24cde9836350b9d80148b282eeaa188b196c2358d4ffd7006cbpng 前端技术框架层出不穷日新月异,很多公司都招了一些专业前端人员来编写页面,服务端只做好业务逻辑和数据存储。前后端分离使得各自更加专注自己善长的领域但也有很多不是所谓的坑等着我们。
公司服务端是通过springboot提供restful接口,前端是react js 通过http请求服务端。当接口参数是一个复杂对象通过对象传输服务端无法接收到对象中的值
接口:
参数:
evalAnswerDOs 始终是个空对象。
前端:1.设置http请求头 Content-Type:application/jsoncharset=utf-8
服务端:
接口参数 增加@RequestBody注解 表示接收的是一个Json对象, 增加consumes表示以当请求的HTTP 头是何种格式的时候进行应对
接口:
入参:
MerItemDO merItemDO 对象
String operatorId
String operatorName
问题:
operatorId 和operatorName 的值服务端能正常接收,merItemDO对象属性里面的值始终都是null
解决方案
第一种:
将3个参数封装成一个对象 如案例一通过 application/json的方式解决
第二种:
将 operatorId和 operatorName 当作 path 传入
前端
第一种: 见案例一
第二种: 前端无需改动
服务端
第一种:见案例一
第二种:
restful 是一个风格而不是一个标准,在springmvc web开发中可以说是兴起于Rails的一种优雅的URI表述方式,是资源的状态和状态转移的描述。
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 @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) {
}
spring rest配置指南
1. springmvc web.xml配置
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
Java代码
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
完整配置
Java代码
<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编写
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<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(@RequestParam("items") Long[] items,HttpServletRequest request,HttpServletResponse response) {
for(int i = 0i <items.lengthi++) {
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/add =>add() 方法是由于add这个方法会被maxthon浏览器当做广告链接过滤掉,因为包含ad字符)
4. jsp 编写
Html代码
<form:form action="${ctx}/userinfo/${userInfo.userId}" method="put">
</form:form>
生成的html内容如下, 生成一个hidden的_method=put,并于web.xml中的HiddenHttpMethodFilter配合使用,在服务端将post请求改为put请求
Java代码
<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/, 重写规则如下
Xml代码
<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,不知会不会在下一版本发布
在线DEMO地址: http://demo.rapid-framework.org.cn:8080/springmvc_rest_demo/userinfo