RESTful开发风格

REST与RESTful

  • REST-表现层状态转换,资源在网络中以某种表现形式进行状态转移
  • RESTful是基于REST理念的一套开发风格,是具体的开发规则

RESTful开发风格
RESTful开发规范

  • 使用URL作为用户交互入口
  • 明确的语义规范(GET|POST|PUT|DELETE)
  • 只返回数据(JSON|XML),不包含任何展现
    RESTful开发风格
    配置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\":\"返回查询结果\"}";
    }
}

RESTful开发风格

<!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;
    }

RESTful开发风格
RESTful开发风格

@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小时。
RESTful开发风格
浏览器跨域访问
浏览器同源策略

  • 同源策略阻止从一个域加载的脚本去获取另一个域上的资源
  • 只要协议、域名、端口有任何一个不同,都被当作是不同的域
  • 浏览器Console看到Access-Control-Allow-Origin就代表跨域了
    RESTful开发风格
    RESTful开发风格
    Spring MVC跨域访问
    CORS跨域资源访问
  • CORS是一种机制,使用额外的HTTP头通知浏览器可以访问其他域
  • URL响应头包含Access-Control-*指明请求允许跨域

Spring MVC解决跨域访问

  • @CrossOrigin - Controller跨域注解
  • mvc:cors - Spring MVC全局跨域配置

方法一
RESTful开发风格

@RestController
@RequestMapping("/restful")
@CrossOrigin(origins = {"http://localhost:8080"},maxAge = 3600)
//请求缓存时间为3600秒 预检请求结果进行缓存 一小时内同样的put请求再次发送的时候就不需要发送预检请求,直接发送实际请求 一个小时过了再发预检请求 降低服务器压力  对预检请求的处理结果进行缓存
public class RestfulController {}

RESTful开发风格
统一对跨境域名进行管理
在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都是不生效的。

上一篇:读懂RESTful风格


下一篇:SpringMVC restful编程风格 URL