REST与RESTful
- REST-表现层状态转换,资源在网络中以某种表现形式进行状态转移
- RESTful是基于REST理念的一套开发风格,是具体的开发规则
RESTful开发规范
- 使用URL作为用户交互入口
- 明确的语义规范(GET|POST|PUT|DELETE)
- 只返回数据(JSON|XML),不包含任何展现
配置pop.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--
context:component-scan 标签作用
在Spring IOC初始化过程中,自动创建并且管理com.imooc.springmvc及子包中
拥有以下注解对象
@Repository 通常使用这个的类都是与数据发生直接交互的类
@Service 业务逻辑类,通常放在xxxService
@Controller 控制器类 Spring控制器
@Component 不好区分类型使用这个
-->
<!--注解形式进行开发 管理包范围-->
<context:component-scan base-package="com.imooc.restful"/>
<!--开启SpringMVC注解模式-->
<mvc:annotation-driven>
<!--消息转换器-->
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--response.setContentType=("text/html;charset=utf-8")-->
<!--浏览器按照UTF-8的方式解决HTML中文乱码问题-->
<value>text/html;charset=utf-8</value>
<!-- 产生json就会使用utf-8格式展示数据 -->
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--将图片/js/css等静态资源排除在外,可提高执行效率-->
<mvc:default-servlet-handler/>
</beans>
修改web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<!--
DispatcherServlet是Spring MVC最核心的对象
DispatcherServlet用于拦截Http请求,
并根据请求的URL调用与之对应的controller方法,来完成Http请求的处理
-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--在web应用启动时自动创建spring Ioc容器
并且初始化DispatcherServlet
-->
<load-on-startup>0</load-on-startup>
</servlet>
<!--映射-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--"/"代表拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>characterFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterFilter</filter-name>
<!--映射地址-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--如果不增加下边这个过滤器,Spring MVC无法接收到PUT和DELETE所传递的参数-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
RESTful是编码风格
@RestController
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
// @ResponseBody
public String doGetRequest() {
return "{\"message\":\"返回查询结果\"}";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTFUL实验室</title>
<script src="jquery-3.5.1.js"></script>
<script>
$(function () {
$("#btnGet").click(function () {
$.ajax({
url: "/restful/request",
type: "get",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
$(function () {
$("#btnPost").click(function () {
$.ajax({
url: "/restful/request/100",
type: "post",
data: "name=lily&age=23",
dataType: "json",
success: function (json) {
$("#message").text(json.message + json.id);
}
})
})
})
$(function () {
$("#btnPut").click(function () {
$.ajax({
url: "/restful/request",
type: "put",
data: "name=lily&age=23",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
$(function () {
$("#btnDelete").click(function () {
$.ajax({
url: "/restful/request",
type: "delete",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
$(function () {
$("#btnPersons").click(function () {
$.ajax({
url: "/restful/persons",
type: "get",
dataType: "json",
success: function (json) {
console.info(json)
for (var i = 0; i < json.length; i++) {
var p = json[i];
$("#divPersons").append("<h2>" + p.name + "-" + p.age + "-" + p.birthday + "</h2>")
}
}
})
})
})
</script>
</head>
<body>
<input type="button" id="btnGet" value="发送Get请求">
<input type="button" id="btnPost" value="发送Post请求">
<input type="button" id="btnPut" value="发送Put请求">
<input type="button" id="btnDelete" value="发送Delete请求">
<h1 id="message"></h1>
<hr>
<input type="button" id="btnPersons" value="查询所有人员">
<div id="divPersons"></div>
</body>
</html>
RestController注解与路径变量
@RestController
@RequestMapping("/restful")
//@CrossOrigin(origins = {"http://localhost:8080"},maxAge = 3600)
public class RestfulController {
@GetMapping("/request")
// @ResponseBody
public String doGetRequest() {
return "{\"message\":\"返回查询结果\"}";
}
@PostMapping("/request/{rid}")
// @ResponseBody
public String doPostRequest(@PathVariable("rid") Integer requestId, Person person) {
System.out.println(person.getName() + ":" + person.getAge());
return "{\"message\":\"数据新建成功\",\"id\":" + requestId + "}";
}
@PutMapping("/request")
// @ResponseBody
public String doPutRequest(Person person) {
System.out.println(person.getName() + ":" + person.getAge());
return "{\"message\":\"数据更新成功\"}";
}
}
简单请求与非简单请求
- 简单请求是指标准结构的HTTP请求,对应GET/POST请求
- 非简单请求是复杂要求的HTTP请求,指PUT/DELETE、扩展标准请求
- 两者最大区别是非简单请求发送前需要发送预检请求
<script>
$(function () {
$("#btnPost").click(function () {
$.ajax({
url: "/restful/request/100",
type: "post",
data: "name=lily&age=23",
dataType: "json",
success: function (json) {
$("#message").text(json.message + json.id);
}
})
})
})
</script>
<!--如果不增加下边这个过滤器,Spring MVC无法接收到PUT和DELETE所传递的参数-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
源码在上边。
Jackson
不用手动拼接字符串。返回对象就会自动转换为JSON
需要在方法上添加@ResponseBody
或者在类上添加@RestController
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.9</version>
<type>bundle</type>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.9</version>
</dependency>
@GetMapping("/person")
@ResponseBody
public Person findByPersonId(Integer id) {
Person p = new Person();
if (id == 1) {
p.setName("张三");
p.setAge(23);
} else if (id == 2) {
p.setName("李四");
p.setAge(28);
}
return p;
}
@GetMapping("/persons")
@ResponseBody
public List<Person> findByPersons() {
List<Person> personList = new ArrayList<>();
Person p1 = new Person();
p1.setName("张三");
p1.setAge(23);
p1.setBirthday(new Date());
personList.add(p1);
Person p2 = new Person();
p2.setName("李四");
p2.setAge(28);
p2.setBirthday(new Date());
personList.add(p2);
return personList;
}
HTML代码在上边
日期类型设置
public class Person {
private String name;
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss sss", 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;
}
}
日期转换需要加上8小时。
浏览器跨域访问
浏览器同源策略
- 同源策略阻止从一个域加载的脚本去获取另一个域上的资源
- 只要协议、域名、端口有任何一个不同,都被当作是不同的域
- 浏览器Console看到Access-Control-Allow-Origin就代表跨域了
Spring MVC跨域访问
CORS跨域资源访问 - CORS是一种机制,使用额外的HTTP头通知浏览器可以访问其他域
- URL响应头包含Access-Control-*指明请求允许跨域
Spring MVC解决跨域访问
- @CrossOrigin - Controller跨域注解
- mvc:cors - Spring MVC全局跨域配置
方法一
@RestController
@RequestMapping("/restful")
@CrossOrigin(origins = {"http://localhost:8080"},maxAge = 3600)
//请求缓存时间为3600秒 预检请求结果进行缓存 一小时内同样的put请求再次发送的时候就不需要发送预检请求,直接发送实际请求 一个小时过了再发预检请求 降低服务器压力 对预检请求的处理结果进行缓存
public class RestfulController {}
统一对跨境域名进行管理
在applicationContext.xml中添加
<mvc:cors>
<!-- 只要是远程域名访问/restful为前缀的url上都会被这个策略管理 -->
<mvc:mapping path="/restful/**"
<!-- 允许远程访问的域名 多个域名使用逗号分隔-->
allowed-origins="http://localhost:8080,http:www.baidu.com"
max-age="3600"/>
</mvc:cors>
如果当前应用是一个专用于WebAPI 只对外提供WEB数据服务的应用,那么需要进行mvc:cors全局配置
如果只是个别的controller需要对外暴露服务的话推荐使用@CrossOrigin
如果两个都配置了,那么运行时,会以注解配置为准。
CORS只是在浏览器中的安全策略。
在小程序,APP都是不生效的。