一、RESTful开发风格
1.1 传统Web应用的困境
客户端必须是支持HTML的浏览器,
但是,随着互联网飞速发展,诞生的微信小程序、APP等其他的应用客户端是不支持HTML的 。
以上的困境,怎么破?
一种全新的开发理念,应运而生:RESTful开发风格。
1.2 REST与RESTful开发风格
1.REST理念
表现层状态转换(Representational State Transfer),资源在网络中,以某种表现形式进行状态转移。
REST理念:
是 Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种软件架构风格,更简洁。例如,Amazon.com提供接近REST风格的Web服务执行图书查询。
2.RESTful开发风格
基于REST理念的一套开发风格,是具体的开发规则和约束。不是技术,只是风格。
不同种类的客户端,如何与服务器进行交互呢?
图示:
关键的不同:
- 服务器:返回的不再是某一个HTML的文本,而是像JSON或XML这样的数据,不包含任何与展现相关的内容。
- 客户端:拿到上述数据后进行展现、渲染。
本质:前后端分离
- 前端:只负责视图界面开发
- 后端:只专注业务逻辑
1.3 RESTful开发规范:3条
其中:
- Web环境下,只支持GET、POST两种请求。所以,后两者平时不怎么常见。
1.4 RESTful 对URI的命名要求:4个
主要针对的是URI:
URI
其实就是URL除去域名的部分
二、用Spring MVC开发一个RESTful的Web应用
2.1 前期基础的项目结构
新建一个标准的Maven工程:
将上述工程,增加Web应用的能力:
自动创建启动项:
蓝色圆心的文件说明,该工程,已经增加Web应用的能力:
配置Tomcat:
因为之前已创建了模板:
Tomcat配置,已经完成:
2.2 三个配置文件中进行配置
1.在工程的配置文件pom.xml
中:
引入spring-mvc框架的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId>
<artifactId>restful</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
</project>
2.在Web应用的配置文件web.xml
中
配置Spring MVC框架的核心类DispatcherServlet
,以及CharacterEncodingFilter
:
DispatcherServlet
:在Web应用启动时,自动的加载classpath:applicationContext.xml
文件,对Spring MVC进行初始化CharacterEncodingFilter
:用于将POST请求体中,所有的字符集设置为UTF-8
<?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_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<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>
</web-app>
3.在Spring配置文件applicationContext.xml
中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--1.因为Spring IoC 的实例化创建对象、属性注入,均是采用注解形式-->
<context:component-scan base-package="com.imooc.restful"/>
<!--2.开启Spring MVC的注解模式。同时,额外创建了消息转换器:响应输出到浏览器上,解决其中文乱码问题-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--3.将静态资源排除在外-->
<mvc:default-servlet-handler/>
</beans>
2.3 编写业务代码
1.
新建控制器包com.imooc.restfulcontroller
,新建一个控制器类RestfulController
:
package com.imooc.restfulcontroller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("restful")
public class RestfulController {
@GetMapping("/request")
@ResponseBody // 通过放进响应体中,直接在浏览器客户端上输出结果
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}"; // 返回的是JSON格式的字符串;双引号中如果包括双引号,就要使用转义字符 \"
}
}
在启动Tomcat之前,需要进行依赖jar包的手动发布,不能忘记。
启动Tomcat:
浏览器:
JSON格式的字符串已成功显示在页面上:
2.
新问题:只是JSON格式的字符串,显示在页面上有什么用?
其实,通过URL访问得到的结果,就是只有数据,没有任何展现。
如果想要成功展现,还需要客户端的支持。比如,一个HTML的页面、一个APP、一个微信小程序。它们提供了与URL交互的功能。
以HTML页面为例,页面运行过程中,想得到URL返回的数据,该用什么技术呢?
Ajax。能在页面中异步的与URL发送请求、得到响应的结果,用于局部刷新。
接下来,模拟客户端页面,与RESTful交互的过程。
2.4 开发HTML的客户端,模拟与服务端的RESTful进行交互
上节:开发了一个controller,实现了标准的RESTful风格。
本节:开发HTML的客户端,从而与服务端的RESTful进行交互。
1.
将jquery的js包,复制到webapp目录下。因为后续在HTML页面中,会使用jquery的Ajax函数,向controller发送HTTP请求。
/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";
......
jquery与ajax的区别:
1)Ajax是一门技术,它提供了异步更新的机制,使用客户端与服务器间交换数据而非整个页面文档,实现页面的局部更新。
2)jQuery是一个框架,它对JS进行了封装,使其更方便使用。jQuery使得JS(JavaScript)与Ajax的使用更方便。
2.
在webapp目录下,新建一个标准的HTML页面,作为HTTP请求的发起者:
- 通过点击这个按钮,利用Ajax函数,向服务器发送一个HTTP请求:
function ()
是页面就虚函数(?),即在页面加载完以后,开始运行。- 绑定单击事件,并编写事件处理程序。
- 当jquery拿到返回的json字符串,便会自动的在success这个回调函数中,将json字符串转换为json对象。并且,在success这个回调函数中,可以直接进行访问。
- 每次点击以后,都让服务器返回的数据填充在message的h1标题中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTful实验室</title>
<script src="jquery-3.3.1.min.js"></script>
<!--下面是一个标准的Ajax调用-->
<script>
$(function () {
$("#btnGet").click(function () {
$.ajax({
url:"/restful/request",
type:"get",
dataType:"json",
success:function (json){
$("#message").text(json.message)
}
})
})
})
</script>
</head>
<body>
<input id="btnGet" type="button" value="发送Get请求"/>
<h1 id="message"></h1>
</body>
</html>
浏览器:
点击“发送”按钮,结果如下:
问题:乱码。不是已经设置了响应中的字符集是utf-8了吗?(在Web应用的配置文件web.xml
中,使用过滤器CharacterEncodingFilter)
原因:
原来是jquery,通过Ajax函数发送请求以后,返回的响应体中,针对application/json
,并没有设置字符集。
解决:
在服务器端,针对application/json
,来修改其字符集即可:
浏览器:
点击“发送”按钮,结果如下:成功显示。
以及:
综上所述,模拟了HTML5客户端,向服务器发送请求的过程。核心:使用了Ajax函数。
其他的客户端,比如小程序、APP,也是这个过程,只不过使用的函数是不一样的。
同时,对服务器来说,不管你用的哪种客户端,都一视同仁,返回的都是相同的数据。至于怎么展现,是你们客户端的事情。
即,前后端分离。
2.5 RESTful的特性:针对不同类型的请求,来做不同的操作
- 查询----Get
- 新增----Post
- 修改----Put
- 删除----Delete
在controoller控制层的RestfulController
类中:
程序后台都有了对应的方法:
@Controller
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
@ResponseBody // 通过放进响应体中,直接在浏览器客户端上输出结果
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}"; // 返回的是JSON格式的字符串;双引号中如果包括双引号,就要使用转义字符 \"
}
@ResponseBody
@PostMapping("/request") // 因为请求的类型不同,所以不冲突
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.3.1.min.js"></script>
<!--下面是一个标准的Ajax调用-->
<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",
type:"post",
dataType:"json",
success:function (json){
$("#message").text(json.message)
}
})
})
})
$(function () {
$("#btnPut").click(function () {
$.ajax({
url:"/restful/request",
type:"put",
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)
}
})
})
})
</script>
</head>
<body>
<input id="btnGet" type="button" value="发送Get请求"/>
<input id="btnPost" type="button" value="发送Post请求"/>
<input id="btnPut" type="button" value="发送Put请求"/>
<input id="btnDelete" type="button" value="发送Delete请求"/>
<h1 id="message"></h1>
</body>
</html>
浏览器:
点击“发送Post请求”按钮,结果如下:
综上所述,成功用Spring MVC开发一个RESTful的Web应用。
上述不足:
(1)每个方法头上,都有一个@ResponseBody
注解,重复麻烦。
返回的是数据。即让返回的字符串,输出到响应体中
(2)RESTful风格标准的URL允许出现数字作为id,这种情况如何获取参数呢
(3)返回值,用的是拼接字符串的形式
一个键值对还可以,但如果是一个复杂对象或大的集合呢?拼接工作量太大。
三、RESTful 的基本使用
3.1 @Rest Controller注解
解决第一个不足:
每个方法头上,都有一个@ResponseBody
注解,重复麻烦。
1.@RestController
注解=@Controller
注解 + @ResponseBody
注解
位置:放在控制层类的头上,用于代替以前的@Controller
注解
用途:描述当前controller类中每一个方法,默认返回的只是数据。即让返回的字符串,输出到响应体中。避免在每个方法头上,都加@ResponseBody
注解了。
意义:简化开发
2.示例
3.2 路径变量
解决第二个不足:
RESTful风格标准的URL中,允许出现数字作为id,如何拿到URL中的数据呢?
Spring MVC支持路径变量。
1.路径变量
定义:存放在URL中的变量,可变的那一部分,
用途:通常指某个具体的id号
POST /article/1 // 创建一个id=1的文章
POST /restful/request/100 // 创建一个id=100的请求
2.用法:注解@PathVariable
POST /restful/request/100 // 创建一个id=100的请求
POST /restful/request/{rid} // 在变量部分,用大括号包裹:里面放变量的名字,自动获取到rid=100
即:
@RestController
@RequestMapping("/restful")
public class RestfulController {
@PostMapping("/request/{rid}")
public String doPostRequest(@PathVariable("rid") Integer requestId){ // 将路径变量rid与后面的方法参数绑定
return "{\"message\":\"数据新建成功\",\"id\":"+requestId+"}";
}
}
3.示例
编码:
浏览器:
成功拿到了URL中的数字100
后续,客户端对上述数据进行展现即可。
3.3 简单请求与非简单请求
1.定义
2.非简单请求的发送过程:
预检请求:让服务器预先处理,对于不符合要求的请求,直接挡在外头,这样实际数据就不会发送。这有利于减轻网络传输、服务器的压力。
图示:
类比生活中的形象例子:
送快递:
- 优质的顺丰送快递:------ 提前给你打电话:“人在家不?如果能接收的话,这给你送过去啊。”--------- 非简单请求
- 廉价的快递:--------------- 直接给你放附近的快递点、丰巢箱里。------------------------------------------------ 简单请求
3.示例
探讨下:Spring MVC在接收POST请求、Put请求的不同?
(1)
controller层的方法,要接收请求参数,最好的方式是使用实体对象Java bean:
新建实体类Person
:
public class Person {
private String name;
private Integer age;
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;
}
}
在客户端client.html
中:
(2)Spring MVC接收POST请求
成功接收:
(3)Spring MVC接收Put请求:
出错:
这是因为最早的Spring MVC是为网页服务的,所以只支持GET/POST两种请求,不支持PUT/DELETE请求。
- 解决:
目前,Spring MVC为处理此类特殊的非简单请求,提供了一个额外的表单内容过滤器。
在web.xml
中,增加以下FormContentFilter
过滤器后,对非简单请求就能够支持请求参数的获取了:
<?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_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<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>
<filter>
<filter-name>formContentFilter</filter-name> // 增加FormContentFilter过滤器
<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>
成功接收:
3.4 JSON序列化
学习如何用Spring MVC框架与jackson组件,进行Java对象的序列化输出?
3.4.1 三个步骤
(1)引入三个依赖:分别是jackson的core、databind、annotations;
jackson是世界上使用范围最广、效率最高的一款JSON序列化组件。
(2)controller层方法定义时,不再返回String或ModelAndView,而是返回需要JSON序列化的Java对象
(3) 日期时间输出时,要用@JsonFormat
注解进行格式化输出
@JsonFormat
:不仅可以对日期时间进行格式化输出,而且还能对货币、数字等格式化输出
其中:jackson组件的推荐版本
官网:建议使用2.9版本以上,否则会有严重的安全风险:
3.4.2 引入jackson组件的三个依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId>
<artifactId>restful</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.9</version>
</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>
</dependencies>
</project>
Spring MVC非常智能,只要引入jackson组件,不需要任何配置,就能自动提供json序列化的服务。
3.4.3 方法定义时,只需返回需要JSON序列化的Java对象
1.查询的是单个对象
jason在Spring MVC中的使用:
@RestController // @Controller + @ResponseBody
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/person") // 因为Spring MVC会自动的通过刚才配置jackson依赖,对实体对象进行序列化输出。你就不用手动拼接JSON字符串了。
public Person findByPersonId(Integer id) { // 返回的是实体对象?怎么不是String,或ModelAndView呢?
Person p = new Person();
if (id == 1) {
p.setName("Lily");
p.setAge(23);
} else if (id == 2) {
p.setName("Smith");
p.setAge(22);
}
return p;
}
}
在Tomcat启动之前,因为增加了三个新的依赖,所以需要手动的将其放到发布目录中;
- 注意:IDEA有时候,你Maven加入了依赖,刷新了没有报错,但是左侧就是没有成功读取并添加进依赖列表中。重启IDEA即可。
启动Tomcat:
浏览器:
2.特殊场景:如果查询的不是单个对象,而是多个对象,该如何返回呢?
使用列表List<Person>
进行返回接收,并自动将List<Person>
,转换为一个JSON数组,包含了每一个JSON对象序列化后的JSON字符串。
编码:
@RestController // @Controller + @ResponseBody
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/persons")
public List<Person> findPersons(){
List list = new ArrayList();
Person p1 = new Person();
p1.setName("Lily");
p1.setAge(23);
Person p2 = new Person();
p2.setName("Smith");
p2.setAge(22);
list.add(p1);
list.add(p2);
return list;
}
}
浏览器:
该JSON字符串,本质先是个数组,在数组中按照前后顺序,对应了两个JSON对象
[{"name":"Lily","age":23},{"name":"Smith","age":22}]
3.4.4 客户端对上述JSON数据进行使用、提取
上述数据,如何在客户端进行使用,并提取出来呢?
在HTML5中,使用Ajax与其交互。
1.在浏览器控制台中打印看下
在client.html
中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTful实验室</title>
<script src="jquery-3.3.1.min.js"></script>
<!--下面是一个标准的Ajax调用-->
<script>
...
$(function () {
$("#btnPersons").click(function () {
$.ajax({ // 发送Ajax请求,用于局部刷新
url:"/restful/persons",
type: "get",
dataType: "json",
success:function (json) {
console.info(json); // 在浏览器控制台中打印看下
}
})
})
})
</script>
</head>
<body>
<input id="btnGet" type="button" value="发送Get请求"/>
<input id="btnPost" type="button" value="发送Post请求"/>
<input id="btnPut" type="button" value="发送Put请求"/>
<input id="btnDelete" type="button" value="发送Delete请求"/>
<h1 id="message"></h1>
<hr>
<input id="btnPersons" type="button" value="查询所有人员">
<div id="divPsersons"></div>
</body>
</html>
浏览器:
返回的响应头中,成功拿到:
浏览器控制台:
成功打印输出:
2.后续在客户端上,直接提取就可以:
在client.html
中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTful实验室</title>
<script src="jquery-3.3.1.min.js"></script>
<!--下面是一个标准的Ajax调用-->
<script>
...
$(function () {
$("#btnPersons").click(function () {
$.ajax({ // 发送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]; // 循环提取出每一个JSON对象
$("#divPsersons").append("<h2>" + p.name + "-" + p.age + "</h1>"); // 将每一个JSON对象的数据,追加到下面的标签体中
}
}
})
})
})
</script>
</head>
<body>
<input id="btnGet" type="button" value="发送Get请求"/>
<input id="btnPost" type="button" value="发送Post请求"/>
<input id="btnPut" type="button" value="发送Put请求"/>
<input id="btnDelete" type="button" value="发送Delete请求"/>
<h1 id="message"></h1>
<hr>
<input id="btnPersons" type="button" value="查询所有人员"> // 用户点击这个按钮
<div id="divPsersons"></div> // 将相关数据追加到该标签中
</body>
</html>
浏览器:
客户端成功对JSON数据进行了使用、提取、解析。
3.4.5 日期时间等的格式化处理:
1.一个小坑
编码:
增加“生日”这个实体对象的属性:
浏览器:
2.jackson提供了注解@JsonFormat
进行格式化处理
@JsonFormat
:是jackson内置的JSON格式化输出的注解,用于按照格式进行输出。
(1)
增加属性注解;
在浏览器中:
确实将日期(总秒数)转换成了格式化的日期形式:
(2)新问题:现在明明是下午4:00,怎么浏览器显示却是早上8:00?相差了8个小时?
因为默认的是按照格林尼治时间,即英国伦敦天文台的时区作为起点。中国与英国相差了8个时区:
解决:
增加8个时区即可
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date birthday;
浏览器:
成功得到了准确的日期时间:
四、浏览器的跨域访问CORS
CORS,只是在浏览器(web端)中的安全策略。
RESTful在跨域中的应用。
浏览器的跨域访问根源,来源于浏览器的同源策略。
4.1 浏览器的同源策略
1.定义
说人话,就是只能访问同一域名下的资源,不能跨域名访问。
目的:保证网站安全、保护资源
比如:
有两个不同的网站,不同的域名在不同的服务器上。网站A---------->网站B,是不被允许的。
生活案例:
一个黑客,在自己的JavaScript中,模拟了十万个人,对同一网站同时发起Ajax请求,导致瞬间瘫痪。
2.示例:只能访问同一域名下的资源,不能跨域名访问
(1)
因为请求的发起者,与目标的URL,在同一个域名下。
所以,数据资源能成功访问。
(2)
如果请求的发起者,与目标的URL,不在同一个域名下。
那么,数据就不能成功访问。比如:
将两个客户端的资源client.html
与jquery
复制到桌面上:
编辑下client.html
里面发送请求得到地址:保存。
并用Chrome浏览器打开:
发现这是通过系统本地直接打开的,地址栏的URL,与要访问的上面红框的URL是不同源的
3.同源策略的示例:协议、域名、端口必须一致
4.HTML中允许跨域的标签
HTML中默认,以下三个标签不受同源策略的约束:
4.2 Spring MVC实现跨域访问的两种方式
1.CORS 跨域资源访问
原理:
2.Spring MVC实现跨域访问的两种方式
其中:
- 跨域注解:只个别的controller类需要对外暴露服务
- XML全局配置:如果当前应用是一个专用于Web api,也就是只对外提供数据服务的应用
4.3 Spring MVC实现跨域访问:@CrossOrigin注解
1.
在另外一个工程中,模拟使用另外一个域:http://localhost:8080
,即只换了个端口。
重新配置工程restful_8080
:
file|projectstructure
- Tomcat
浏览器:
对于8080工程的本域下的资源:能够成功访问到:
2.问题
在8080工程的client.html`中,修改发送请求的地址,并保存。
即,向另外一个不同的80工程的域发送请求:
(因为http://localhost/restful/persons
端口号默认是80,所以是不同的域)
在服务的提供端80工程,启动Tomcat。看其他工程的域是否能访问到自己。
浏览器结果:
8080工程---->80工程,是失败的。
3.解决:
在服务的提供端80工程,给其他域授权:
在contoller上面,增加一个说明跨域访问范围的注解:
浏览器:
8080工程的域,成功跨域访问到了80工程的资源,说明上述跨域注解生效了:
本质:
4.
如果有多个域名,都需要跨域授权呢?
@RestController
@RequestMapping("/restful")
@CrossOrigin(origins = {"http://localhost:8080","http://www.imooc.com"})
public class RestfulController {
@GetMapping("/request")
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
...
当然,偷懒的办法:
实际不常用,不安全
@RestController
@RequestMapping("/restful")
@CrossOrigin(origins = "*") // 所有协议、所有域名、所有端口都能访问
public class RestfulController {
@GetMapping("/request")
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
...
5.设置预检请求的缓存时间:
因为预检请求的授权逻辑不会轻易改变,所以将预检请求的处理结果进行缓存(如1h),这样非简单请求在”暂时免检期内“就不用再发两次请求了,直接发送实际请求。
好处:降低服务器的压力。
@RestController
@RequestMapping("/restful")
@CrossOrigin(origins = {"http://localhost:8080","http://www.imooc.com"},maxAge = 3600) // 设置预检请求的缓存时间是3600s
public class RestfulController {
@GetMapping("/request")
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
...
4.4 Spring MVC实现跨域访问:XML全局配置
把原先写在跨域注解中的参数信息,都给放进XML文件中:
<?xml version="1.0" encoding="UTF-8"?>
<beans ..>
<!--1.因为Spring IoC 的实例化创建对象、属性注入,均是采用注解形式-->
<context:component-scan base-package="com.imooc.restful"/>
<!--2.开启Spring MVC的注解模式。同时,额外创建了消息转换器:响应输出到浏览器上,解决其中文乱码问题-->
<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>
<!--3.将静态资源排除在外-->
<mvc:default-servlet-handler/>
<!--4.跨域访问的配置-->
<mvc:cors>
<mvc:mapping path="/restful/**"
allowed-origins="http://localhost:8080,http://www.imooc.com"
max-age="3600"/>
</mvc:cors>
</beans>
浏览器:
8080工程的域,成功跨域访问到了80工程的资源,说明上述跨域的XML全局配置生效了:
本质:
Comments | NOTHING