Spring MVC 之 RESTful 架构思想
REST:Representational State Transfer,资源表现层状态转化,是目前最流行的一种互联网软件架构。它结构清晰,标准规范,易于理解,扩展方便,所以得到了越来越多网站的使用。
- 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一个文件、一张图片、一首歌曲或是一段视频等,可以用一个 URI(统一资源定位符)指向它,每种资源都对应一个特定的 URI。要获取这个资源,访问它的 URI 即可,因此 URI 是每一个资源独一无二的的标识符。
- 表现层(Representation):把资源具体内容呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 TXT 格式表现,也可以用 HTML、XML、JSON 格式表现,甚至可以采用二进制的格式。
- 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP 协议是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种方式,让服务器端发生“状态转化”,而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。
特点:
-
url 更加简洁,将参数通过 url 传到服务端;
- 非 RESTful 的 URL:http://localhost:8080/query?id=1
- RESTful 风格的 URL:http://localhost:8080/query/1
-
有利于不同系统之间的资源共享,只需要遵守规范,不需要做其他的配置就能达到资源共享;
Restful 具体来说就是四种表现形式,HTTP 协议中四个表示操作方式的动词:GET、POST、PUT、DELETE,分别对应四种基本操作:
- GET 用于获取资源;
- POST 用来创建或存储资源;
- PUT 用于修改资源;
- DELETE 用来删除资源;
但是我们知道 form 表单只支持 GET 与 POST 请求,并不支持 DELETE 和 PUT,如何解决这个问题呢?添加 HiddenHttpMethodFilter 过滤器,可以将 POST 请求转为 PUT 或 DELETE。
过滤器的实现原理大致是:检测请求参数中是否包含 _method
这个参数,如果包含,则获取其值,然后判断是哪种操作后继续传递。
具体步骤:
1、form 表单添加隐藏标签,设置 name="_method"
,value="PUT/DELETE"
;
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/put" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="修改"/>
</form>
</body>
</html>
2、在 web.xml 中配置 HidddenHttpMethodFilter;
<filter>
<filter-name>HidddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HidddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、编写控制器的业务方法;
@Controller
public class TestHandler {
@PutMapping("/put")
@ResponseBody
public String PUTMethod(){
return "修改成功!";
}
}
启动 Tomcat 服务器,运行程序:
请求响应成功!
如果直接在地址栏访问 http://localhost:8080/put
,发送的是 GET 类型的请求,此时请求返回错误代码 405.
上面是通过 form 表单来发送 PUT/DELETE 请求类型,若使用 AJAX 则不需要这么复杂,下面我们通过 AJAX 以 restful 的方式实现数据的增删改查操作。
1、客户端发送请求的 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<html>
<head>
<title>restful风格</title>
<script type="text/javascript" src="js/jquery-2.0.0.min.js"></script>
<script type="text/javascript">
$(function () {
//处理GET类型的请求
$("#get").click(function () {
var id = $("#get_id").val();
$.ajax({
url:'/getById/'+id,
type:"GET",
dataType:"json",
success:function (data) {
alert("学号:" + data.id + ",姓名:" + data.name + ",年龄:" + data.age);
location.reload(true);
},
//返回值为空时,null无法转化成json,故报错
error:function () {
alert("该用户不存在!");
location.reload(true);
}
})
});
//处理POST类型的请求
$("#post").click(function () {
var id = $("#post_id").val();
var name = $("#post_name").val();
var age = $("#post_age").val();
$.ajax({
url:"/post/"+id+"/"+name+"/"+age,
type:"POST",
dataType:"json", //返回的数据类型
success:function (data) {
alert(data);
location.reload(true);
}
})
});
//处理PUT类型的请求
$("#put").click(function () {
var id = $("#put_id").val();
var name = $("#put_name").val();
var age = $("#put_age").val();
$.ajax({
url:"/put/"+id+"/"+name+"/"+age,
type:"PUT",
dataType:"json", //返回的数据类型
success:function (data) {
alert("学号:" + data.id + ",姓名:" + data.name + ",年龄:" + data.age);
location.reload(true);
},
error:function () {
alert("该用户不存在!");
location.reload(true);
}
})
});
//处理DELETE类型的请求
$("#delete").click(function () {
var id = $("#delete_id").val();
$.ajax({
url:"/delete/"+id,
type:"DELETE",
dataType:"json", //返回的数据类型
success:function (data) {
alert(data);
location.reload(true);
}
})
});
})
</script>
</head>
<body>
GET:<input type="text" id="get_id"/><input type="button" value="查询" id="get">
<hr/>
POST:<br/>
编号:<input type="text" id="post_id"><br/>
姓名:<input type="text" id="post_name"><br/>
年龄:<input type="text" id="post_age"><br>
<input type="button" value="提交" id="post">
<hr/>
PUT:<br/>
编号:<input type="text" id="put_id"><br/>
姓名:<input type="text" id="put_name"><br/>
年龄:<input type="text" id="put_age"><br>
<input type="button" value="修改" id="put">
<hr/>
DELETE:<br/>
<input type="text" id="delete_id"><input type="button" value="删除" id="delete">
</body>
</html>
2、创建 Student 实体类
package com.trainingl.entity;
public class Student {
private Integer id;
private String name;
private Integer age;
public Student(){
}
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
3、编写控制器的业务方法;
package com.trainingl.handler;
import com.trainingl.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
public class RestfulHandler {
private static Map<Integer,Student> studentMap;
static {
studentMap = new HashMap<Integer,Student>();
studentMap.put(1,new Student(1,"张三",23));
studentMap.put(2,new Student(2,"李四",24));
studentMap.put(3,new Student(3,"王五",25));
}
@RequestMapping(value = "/getById/{id}",method = RequestMethod.GET)
@ResponseBody
public Student httpGet(@PathVariable("id")int id){
Student student = studentMap.get(id);
return student;
}
@PostMapping("/post/{id}/{name}/{age}")
@ResponseBody
public String httpPost(@PathVariable("id")int id,@PathVariable("name")String name,@PathVariable("age")int age){
studentMap.put(id,new Student(id,name,age));
return "添加成功!";
}
@PutMapping("/put/{id}/{name}/{age}")
@ResponseBody
public Student httpPut(@PathVariable("id")int id,@PathVariable("name")String name,@PathVariable("age")int age){
Student student = studentMap.get(id);
if(student == null){
return null;
} else {
Student stu = new Student(id,name,age);
studentMap.put(id,stu);
return stu;
}
}
@DeleteMapping("/delete/{id}")
@ResponseBody
public String httpDelete(@PathVariable("id")int id){
if(studentMap.get(id) != null){
studentMap.remove(id);
return "删除成功!";
}else {
return "该用户不存在!";
}
}
}
4、服务器后台返回的对象转化成 JSON 数据,需要在 springmvc.xml 中添加配置
- 消息转换器解决的是后台向前端返回数据的编码问题;
- fastjson 解决 JavaBean 转为 JSON 数据格式不匹配的问题;
<mvc:annotation-driven conversion-service="conversionService">
<!-- 消息转换器 -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
</bean>
<!-- 配置fastjson -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
5、启动 Tomcat 服务器,运行 Web 应用程序,测试不同的请求类型。
至此,Spring MVC 实现了 Restful 架构中不同的业务操作对应不同 HTTP 请求类型。(注:CRUD——POST、GET、PUT、DELETE)