RESTful开发风格
REST与RESTful
- REST(Representational State Transfer)-表现层状态转换,资源在网络中以某种表现形式进行状态转移
- RESTful是基于REST理念的一套开发风格,是具体的开发规则
RESTful传输数据
RESTful开发规范
- 使用URL作为用户交互入口
- 明确的语义规范(GET|POST|PUT|DELETE->查|增|改|删)
- 只返回数据(JSON|XML),不包含任何展现
RESTful命名要求
URI | 说明 | 修改建议 |
---|---|---|
GET /articles?au=lily | 正确用法 | |
GET /a/1 | URI必须具有语义 | GET /student/1 |
POST /createArticle/1 | URI必须使用名词 | POST /article/1 |
GET /articles/author/1 | URI扁平化,不超两级 | GET /articles/author?id=1 |
DELETE /articles/1 | URI名词区分单复数 | GET /articles?au=lily DELETE /article/1 |
开发第一个RESTful应用
RestfulController.java
package com.imooc.restful.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @author Rex * @create 2021-01-12 12:00 */ @Controller @RequestMapping("/restful") public class RestfulController { @GetMapping("/request") @ResponseBody public String doGetRequest(){ return "{\"message\":\"返回查询结果\"}"; } @PostMapping("/request") @ResponseBody public String doPostRequest(){ return "{\"message\":\"数据新建成功\"}"; } @PutMapping("/request") @ResponseBody public String doPutRequest(){ return "{\"message\":\"数据更新成功\"}"; } @DeleteMapping("/request") @ResponseBody public String doDeleteRequest(){ return "{\"message\":\"数据删除成功\"}"; } }
client.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>RESTful实验室</title> <script src="./jquery-3.5.1.min.js"></script> <script> $(function () { $("#btnGet").click(function () { $.ajax({ "url":"/restful/request", "type":"get", "dataType":"json", "success": function (json) { $("#message").text(json.message); } }) }); $("#btnPost").click(function () { $.ajax({ "url":"/restful/request", "type":"post", "dataType":"json", "success": function (json) { $("#message").text(json.message); } }) }); $("#btnPut").click(function () { $.ajax({ "url":"/restful/request", "type":"put", "dataType":"json", "success": function (json) { $("#message").text(json.message); } }) }); $("#btnDelete").click(function () { $.ajax({ "url":"/restful/request", "type":"delete", "dataType":"json", "success": function (json) { $("#message").text(json.message); } }) }); }) </script> </head> <body> <input type="button" value="发送Get请求" id="btnGet"/> <input type="button" value="发送Post请求" id="btnPost"/> <input type="button" value="发送Put请求" id="btnPut"/> <input type="button" value="发送Delete请求" id="btnDelete"/> <h1 id="message"></h1> </body> </html>
结果:
为了解决json乱码问题需要在applicationContext对MediaTypes进行处理
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html; charset=utf-8</value> <value>application/json; charset=utf-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
RestController注解
@RestController = @ResposneBody + @Controller, 默认将String向请求中输出,而不是将请求转发到该路径下
路径变量(@PathVariable)
路径变量:存放在URI中可变的数值
代码示例:
RestfulController.java
@PostMapping("/request/{rid}") public String doPostRequest(@PathVariable("rid") Integer requestId){ return "{\"message\":\"数据新建成功\", \"id\":\""+requestId+"\"}"; }
client.html
$("#btnPost").click(function () { $.ajax({ "url":"/restful/request/100", "type":"post", "dataType":"json", "success": function (json) { $("#message").text(json.message + ":"+json.id); } }) });
结果:
简单请求和非简单请求
- 简单请求是指标准结构的HTTP请求,对应GET/POST请求
- 非简单请求是指复杂要求的HTTP请求,指PUT/DELETE、扩展标准请求
- 两者最大区别是非简单请求发送前需要发送预检请求
非简单请求
非简单请求中参数传递
需要配置FormContentFilter过滤器
代码示例:
web.xml
<filter> <filter-name>formContent</filter-name> <filter-class>org.springframework.web.filter.FormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>formContent</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
RestfulController.java
@PutMapping("/request") public String doPutRequest(Person person){ System.out.println(person.getName()+":"+person.getAge()); return "{\"message\":\"数据更新成功\"}"; }
client.html
$("#btnPut").click(function () { $.ajax({ "url":"/restful/request", "type":"put", "dataType":"json", "data":"name=lily&age=23", "success": function (json) { $("#message").text(json.message); } }) });
结果:
JSON序列化
- 引入jackson-core -> jack核心包
- 引入jackson-databind->用于jackson与目标对象交互
- 引入jackson-annotations用于jackson的注解
tip:日期类型、数字类型、货币类型的处理需要加上@JsonFormat注解,如@JsonFormat(pattern="", timezone=“GMT+8”)
使用示例:
RestController.java
@GetMapping("/persons") public List<Person> findPersons(){ List list = new ArrayList(); Person p1 = new Person(); p1.setName("lily"); p1.setAge(23); p1.setBirthday(new Date()); Person p2 = new Person(); p2.setName("smith"); p2.setAge(22); p2.setBirthday(new Date()); list.add(p1); list.add(p2); return list; }
Person.java
package com.imooc.restful.entity; import com.fasterxml.jackson.annotation.JsonFormat; import java.util.Date; /** * @author Rex * @create 2021-01-12 14:02 */ public class Person { private String name; private Integer age; @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss", timezone = "GMT+8") private Date birthday; 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; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
client.html
<script> //... $("#btnPersons").click(function () { $.ajax({ "url":"/restful/persons", "type":"get", "dataType":"json", "success": function (json) { console.log(json) // $("#message").text(json.message); for (var i = 0; i < json.length; i++) { var p = json[i]; $("#divPersons").append("<h2>"+p.name+"-"+p.age+"-"+p.birthday+"</h2>") } } }) }); </script> <!--...--> <input type="button" id="btnPersons" value="查询所有人员"> <div id="divPersons"></div>
结果:
浏览器的跨域访问
浏览器的同源策略
- 同源策略阻止从一个域加载的脚本去获取另一个域上的资源
- 只要协议、域名、端口号有任何一个不同,都被当作是不同的域
- 浏览器Console看到Access-Control-Allow-Origin就代表跨域了
同源策略示例
源URL | 目标URL | 直接访问? |
---|---|---|
http://imooc.com | https://xxx.com:8080/test | 不能 |
http://imooc.com | https://imooc.com | 不能 |
http://imooc.com | http://abc.imooc.com | 不能 |
http://imooc.com | http://imooc.com:8080 | 不能 |
http://localhost | http://127.0.0.1 | 不能 |
http://imooc.com | http://imooc.com/user/test | 可以 |
HTML中允许跨域的标签
<img>
- 显示远程图片<script>
- 加载远程JS<link>
- 加载远程CSS
SpringMVC跨域访问
CORS跨域资源访问
- CORS是一种机制,使用额外的HTTP头通知浏览器可以访问其他域
- URL响应头包含 Access-Control-*指明请求允许跨域
SpringMVC解决跨域访问
-
@CrossOrigin
- Controller跨域注解代码示例:
80端口下的RestfulController.java:
package com.imooc.restful.controller; import com.imooc.restful.entity.Person; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author Rex * @create 2021-01-12 12:00 */ @RestController @RequestMapping("/restful") //@CrossOrigin(origins = {"http://localhost:8080", "http://www.imooc.com"}) @CrossOrigin(origins = "*", maxAge = 3600)//匹配每一个URL, maxAge->设置预检请求的访问时间 public class RestfulController { //.... }
8080端口下的client.html
$("#btnPersons").click(function () { $.ajax({ "url":"http://localhost/restful/persons", "type":"get", "dataType":"json", "success": function (json) { console.log(json) // $("#message").text(json.message); for (var i = 0; i < json.length; i++) { var p = json[i]; $("#divPersons").append("<h2>"+p.name+"-"+p.age+"-"+p.birthday+"</h2>") } } }) });
结果:
-
<mvc:cors>
- Spring MVC 全局跨域配置applicationContext.xml
<mvc:cors> <mvc:mapping path="/restful/**" allowed-origins="http://localhost:8080, http://www.imooc.com" max-age="3600"/> </mvc:cors>