• SSM - Springboot - MyBatis-Plus 全栈体系(二十一)


    第四章 SpringMVC

    四、RESTFUL 风格设计和实战

    1. RESTFul 风格概述

    1.1 RESTFul 风格简介

    在这里插入图片描述

    • RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议,广泛应用于现代的 Web 服务开发。
    • 通过遵循 RESTful 架构的设计原则,可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰,并且易于使用和理解,它们使用标准的 HTTP 方法和状态码进行通信,不需要额外的协议和中间件。
    • 总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!
      在这里插入图片描述
    • 学习 RESTful 设计原则可以帮助我们更好去设计 HTTP 协议的 API 接口!
    1.2 RESTFul 风格特点
    • 每一个 URI 代表 1 种资源(URI 是名词);
    • 客户端使用 GET、POST、PUT、DELETE 4 个表示操作方式的动词对服务端资源进行操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源;
    • 资源的表现形式是 XML 或者JSON
    • 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
    1.3 RESTFul 风格设计规范
    1.3.1 HTTP 协议请求方式要求
    • REST 风格主张在项目设计、开发过程中,具体的操作符合HTTP 协议定义的请求方式的语义
    操作请求方式
    查询操作GET
    保存操作POST
    删除操作DELETE
    更新操作PUT
    1.3.2 URL 路径风格要求
    • REST 风格下每个资源都应该有一个唯一的标识符,例如一个 URI(统一资源标识符)或者一个 URL(统一资源定位符)。资源的标识符应该能明确地说明该资源的信息,同时也应该是可被理解和解释的!
    • 使用 URL+请求方式确定具体的动作,他也是一种标准的 HTTP 协议请求!
    操作传统风格REST 风格
    保存/CRUD/saveEmpURL 地址:/CRUD/emp
    请求方式:POST
    删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2
    请求方式:DELETE
    更新/CRUD/updateEmpURL 地址:/CRUD/emp
    请求方式:PUT
    查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2
    请求方式:GET
    • 总结
      • 根据接口的具体动作,选择具体的 HTTP 协议请求方式
      • 路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!
    1.4 RESTFul 风格好处
    1.4.1 含蓄,安全
    • 使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。
    1.4.2 风格统一
    • URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。
    1.4.3 无状态
    • 在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。
    1.4.4 严谨,规范
    • 严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。
    1.4.5 简洁,优雅
    • 过去做增删改查操作需要设计 4 个不同的 URL,现在一个就够了。
    操作传统风格REST 风格
    保存/CRUD/saveEmpURL 地址:/CRUD/emp 请求方式:POST
    删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
    更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
    查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET
    1.4.6 丰富的语义

    2. RESTFul 风格实战

    2.1 需求分析
    • 数据结构: User {id 唯一标识,name 用户名,age 用户年龄}
    • 功能分析
      • 用户数据分页展示功能(条件:page 页数 默认 1,size 每页数量 默认 10)
      • 保存用户功能
      • 根据用户 id 查询用户详情功能
      • 根据用户 id 更新用户数据功能
      • 根据用户 id 删除用户数据功能
      • 多条件模糊查询用户功能(条件:keyword 模糊关键字,page 页数 默认 1,size 每页数量 默认 10)
    2.2 RESTFul 风格接口设计
    2.2.1 接口设计
    功能接口和请求方式请求参数返回值
    分页查询GET /userpage=1&size=10{ 响应数据 }
    用户添加POST /user{ user 数据 }{响应数据}
    用户详情GET /user/1路径参数{响应数据}
    用户更新PUT /user{ user 更新数据}{响应数据}
    用户删除DELETE /user/1路径参数{响应数据}
    条件模糊GET /user/searchpage=1&size=10&keywork=关键字{响应数据}
    2.2.2 问题讨论
    • 为什么查询用户详情,就使用路径传递参数,多条件模糊查询,就使用请求参数传递?

    • 误区:restful 风格下,不是所有请求参数都是路径传递!可以使用其他方式传递!

    • 在 RESTful API 的设计中,路径和请求参数和请求体都是用来向服务器传递信息的方式。

      • 对于查询用户详情,使用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。使用路径参数可以明确指定所请求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。
      • 而对于多条件模糊查询,使用请求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果,路径参数的组合和排列可能会很多,不如使用请求参数更加灵活和简洁。
    • 此外,还有一些通用的原则可以遵循:

      • 路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。
      • 请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。
      • 对于敏感信息,最好使用 POST 和请求体来传递参数。
    2.3 后台接口实现
    2.3.1 准备用户实体类
    package com.alex.pojo;
    
    /**
     * projectName: com.alex.pojo
     * 用户实体类
     */
    public class User {
    
        private Integer id;
        private String name;
    
        private Integer age;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    2.3.2 准备用户 Controller
    /**
     * projectName: com.alex.controller
     *
     * description: 用户模块的控制器
     */
    @RequestMapping("user")
    @RestController
    public class UserController {
    
        /**
         * 模拟分页查询业务接口
         */
        @GetMapping
        public Object queryPage(@RequestParam(name = "page",required = false,defaultValue = "1")int page,
                                @RequestParam(name = "size",required = false,defaultValue = "10")int size){
            System.out.println("page = " + page + ", size = " + size);
            System.out.println("分页查询业务!");
            return "{'status':'ok'}";
        }
    
    
        /**
         * 模拟用户保存业务接口
         */
        @PostMapping
        public Object saveUser(@RequestBody User user){
            System.out.println("user = " + user);
            System.out.println("用户保存业务!");
            return "{'status':'ok'}";
        }
    
        /**
         * 模拟用户详情业务接口
         */
        @PostMapping("/{id}")
        public Object detailUser(@PathVariable Integer id){
            System.out.println("id = " + id);
            System.out.println("用户详情业务!");
            return "{'status':'ok'}";
        }
    
    
        /**
         * 模拟用户更新业务接口
         */
        @PutMapping
        public Object updateUser(@RequestBody User user){
            System.out.println("user = " + user);
            System.out.println("用户更新业务!");
            return "{'status':'ok'}";
        }
    
    
        /**
         * 模拟条件分页查询业务接口
         */
        @GetMapping("search")
        public Object queryPage(@RequestParam(name = "page",required = false,defaultValue = "1")int page,
                                @RequestParam(name = "size",required = false,defaultValue = "10")int size,
                                @RequestParam(name = "keyword",required = false)String keyword){
            System.out.println("page = " + page + ", size = " + size + ", keyword = " + keyword);
            System.out.println("条件分页查询业务!");
            return "{'status':'ok'}";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
  • 相关阅读:
    Istio 入门(五):访问控制和流量管理
    铁死亡细胞实验相关抑制剂、激动剂
    Android Studio run main()方法报错
    SystemVerilog学习-09-进程间同步、通信和虚方法
    RIP路由信息协议
    EfficientNet-V1论文阅读笔记
    Spark 3.0 - 7.LR 多分类实现影评预测电影评分与指标评测
    【实用 Python 库】Python glob库:轻松应对文件和目录管理
    薄盒借周杰伦IP卖藏品 车翻在奈雪的茶
    shiro721反序列化漏洞(CVE-2019-12422)原理与漏洞复现和利用(保姆级的详细教程)
  • 原文地址:https://blog.csdn.net/sgsgkxkx/article/details/133616145