23.2 RESTful风格的应用



一、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.htmljquery复制到桌面上:

编辑下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全局配置生效了:

本质:

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 23.2 RESTful风格的应用


Follow excellence, and success will chase you.