一、REST 简介

1、概念

Representational State Transfer——表现层(资源)状态转化。是目前最流行的一种互联网软件架构风格。

它倡导结构清晰、符合标准、易于理解、扩展方便的 Web 架构体系,主张严格按照 HTTP 协议中定义的规范设计结构严谨的 Web 应用架构体系。由于 REST 所倡导的理念让 Web 应用更易于开发和维护,更加优雅简洁,所以正得到越来越多网站的采用。

资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。

表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。

状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP 协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

请求方式作用
GET查询
POST保存
PUT更新
DELETE删除

2.REST 风格的 URL

REST 风格要求我们不要再使用问号键值对的方式携带请求参数,而是从 URL 地址中获取。下面我们进行一下对比:

① 保存操作

URL请求方式
TRADITIONAL URLhttp://localhost:8080/CRUD/saveEmpPOST 请求
RESTFUL URLhttp://localhost:8080/CRUD/empPOST请求

② 删除操作

URL请求方式
TRADITIONAL URLhttp://localhost:8080/CRUD/removeEmp?empId=2GET 请求
RESTFUL URLhttp://localhost:8080/CRUD/emp/2DELETE请求

③ 更新操作

URL请求方式
TRADITIONAL URLhttp://localhost:8080/CRUD/updateEmpPOST 请求
RESTFUL URLhttp://localhost:8080/CRUD/empPUT请求

④ 查询操作(查询单个对象)

URL请求方式
TRADITIONAL URLhttp://localhost:8080/CRUD/getEmp?empId=2GET 请求
RESTFUL URLhttp://localhost:8080/CRUD/emp/2GET请求

3、REST 风格 URL 的好处

① 含蓄,安全

使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称

② 风格统一

URL 地址整体格式统一,从前到后始终都使用斜杠划分各个内容部分,用简单一致的格式表达语义。

③ 无状态

在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。

④ 严谨,规范

严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。

⑤ 简洁,优雅

过去做增删改查操作需要设计 4 个不同的 URL,现在一个就够了

传统风格REST 风格
URL请求方式
/CRUD/saveEmp/CRUD/empPOST
/CRUD/removeEmp?empId=2/CRUD/emp/2DELETE
/CRUD/updateEmp/CRUD/empPUT
/CRUD/editEmp?empId=2/CRUD/emp/2GET

⑥ 丰富的语义

通过 URL 地址就可以知道资源之间的关系。

http://localhost:8080/shop
http://localhost:8080/shop/product
http://localhost:8080/shop/product/cellPhone
http://localhost:8080/shop/product/cellPhone/iPhone

二、SpringMVC 对四种请求方式的支持

1、说明

受 HTML 的限制,只有 GET 请求和 POST 请求是可以直接生成的。

为了生成 PUT 和 DELETE 请求方式我们需要借助一个过滤器:

org.springframework.web.filter.HiddenHttpMethodFilter

这个过滤器可以将 POST 请求转换为 PUT 或 DELETE 等其他形式。

2、HiddenHttpMethodFilter 的使用方法

① 在 web.xml 中进行配置,拦截所有资源。

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

② 在表单隐藏域中通过_method 请求参数附带请求方式名称

1
2
3
4
5
6
7
8
<!-- 将POST请求转为PUT请求 -->
<!-- 表单需要按照HiddenHttpMethodFilter的要求来写 -->
<!-- 要求1:请求本身是必须是POST -->
<!-- 要求2:指定新请求方式的请求参数名称必须是_method -->
<form action="${pageContext.request.contextPath}/update/emp" method="post">
<input type="hidden" name="_method" value="PUT" />
<button type="submit">更新</button>
</form>

③ 通过点击超链接执行删除操作。

这是一个难点,超链接中没有表单隐藏域,所以需要将超链接转换为表单进行提交,这就需要借助于 JavaScript。

3、删除超链接转为 DELETE 请求

① 方案一:把超链接改成表单

1
2
3
<a href="${pageContext.request.contextPath}/removeEmp?empId=${employee.empId}"
>删除</a
>

修改后:

1
2
3
4
5
<form action="${pageContext.request.contextPath}/removeEmp" method="post">
<input type="hidden" name="_method" value="DELETE" />
<input type="hidden" name="empId" value="${employee.empId}" />
<button type="submit">删除</button>
</form>

② 方案二:还是基于超链接修改

[1]提供一个通用的表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 1、提供一个通用的表单,用来将GET请求先转换为POST,然后再发送_method请求参数 -->
<!-- action属性不能写死,将来点击哪一个超链接,目标地址就和那个超链接一致 -->
<form id="commonForm" action="" method="post">
<input type="hidden" name="_method" value="delete" />
</form>

<a class="removeEmp" href="${pageContext.request.contextPath}/remove/emp/1"
>模拟删除</a
><br />
<a class="removeEmp" href="${pageContext.request.contextPath}/remove/emp/2"
>模拟删除</a
><br />
<a class="removeEmp" href="${pageContext.request.contextPath}/remove/emp/3"
>模拟删除</a
><br />

[2]加入 jQuery 环境

images

1
2
3
4
<script
type="text/javascript"
src="${pageContext.request.contextPath}/script/jquery-1.7.2.js"
></script>

[3]编辑 jQuery 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
$(function () {
$('.removeEmp').click(function () {
// 在单击响应函数中获取当前点击的超链接的URL地址
var targetUrl = this.href
console.log(targetUrl)

// 通过id获取到表单的jQuery对象,然后设置action属性值,再提交表单
$('#commonForm').attr('action', targetUrl).submit()

// 取消控件的默认行为
return false
})
})

[4]后端 handler 方法

1
2
3
4
5
@RequestMapping(value="/remove/emp/*", method = RequestMethod.DELETE)
public String removeEmp() {
System.out.println("EmpController...removeEmp()...");
return "target";
}

4、@PathVariable 注解

① 工作机制

[1]URL 地址举例

/get/emp/2

[2]使用方式

@RequestMapping 注解中,使用一对儿大括号把 URL 地址中动态、会变的部分给标记出来
在大括号中给这个变量起个名字
在 handler 方法形参位置使用@PathVariable 注解引用这个名字
@PathVariable 注解修饰方法的一个形参
SpringMVC 会把 URL 地址中匹配出来的具体值从形参这里传入

② 代码举例

[1]一个参数的情况

HTML 代码:

1
<a href="${pageContext.request.contextPath}/get/emp/1">一个参数情况</a><br />

Java 代码:

1
2
3
4
5
6
7
@RequestMapping("/get/emp/{empId}")
public String getEmp(@PathVariable("empId") Integer empId) {

System.out.println("empId = " + empId);

return "target";
}

[2]多个参数的情况

HTML 代码:

1
2
3
<a href="${pageContext.request.contextPath}/send/message/tom/tell/jerry"
>多个参数情况</a
><br />

Java 代码:

1
2
3
4
5
6
7
8
9
10
11
12
// /send/message/tom/tell/jerry
@RequestMapping("/send/message/{foo}/tell/{bar}")
public String sendMessage(
@PathVariable("foo") String foo,
@PathVariable("bar") String bar
) {

System.out.println("foo = " + foo);
System.out.println("bar = " + bar);

return "target";
}

5、实际请求方式和要求请求方式不一致:405 错误

images

实际请求方式:DELETE

要求请求方式:PUT

1
@RequestMapping(value="/remove/emp/*", method = RequestMethod.DELETE)

其他典型错误响应状态码:

400:往往是请求参数导致的错误

403:表示访问目标资源没有权限

404:表示访问不到目标资源

  • URL 地址确实不对
  • Web 应用启动时控制台已经抛出异常,导致整个 Web 应用不可用,访问任何资源都是 404
  • 访问了 WEB-INF 目录下的资源
  • DispatcherServlet 的 url-pattern 配置的是/*
  • 服务器端按照旧代码运行,需要清理、重新部署

405:请求方式不匹配

406:实际请求的 URL 地址扩展名是 html,但是服务器端即将返回 JSON 格式的数据,『挂羊头,卖狗肉』