• 【编程不良人】快速入门SpringBoot学习笔记05---RestFul、异常处理、CORS跨域、Jasypt加密


    1. RestFul

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    1.1 引言

           REST全称是(Resources) Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。" 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

           REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。

    web开发:实现所有功能

    restful:软件设计风格---标准、简洁、层次、优雅,基于rest设计原则和约束的架构被称之为restFul。

    总结:

    • Rest 词:没有更新的技术、组件、服务,但能让web请求能够利用web中的标准和能力更好的描述架构。

    • RestFul: 是一种以网络为基础构架的一种架构风格, 符合Rest设计原则和约束的架构被称为RestFul。

    • Restful 一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    1.2 URL定义

    • 资源:互联网所有的事物都可以被抽象为资源

      • 一首歌、一张图片、数据库一条记录等

    • 资源操作:使用POST(添加)、DELETE(删除)、PUT(修改)、GET(查询),使用不同请求方法对资源进行操作。

      • 删除 delete

      • 查询 get

      • 添加 post

      • 修改 put (修改全部字段)| patch(更新部分字段)

    1.3 传统方式操作资源

           注意:传统的操作是没有问题的,但大神认为是有问题的,有什么问题呢?你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser,修改的时候用了updateUser,其实完全没有这个必要,我使用了get请求就是查询、使用post请求就是新增的请求、PUT就是修改、delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful。

    1.4 使用RESTful操作资源

    • 【GET】 /users # 查询用户信息列表

    • 【GET】 /users/1001 # 查看某个用户信息

    • 【POST】 /users # 新建用户信息

    • 【PUT】 /users/1001 # 更新用户信息(全部字段)

    • 【PATCH】 /users/1001 # 更新用户信息(部分字段)

    • 【DELETE】 /users/1001 # 删除用户信息

    1.5 Rest API设计风格原则

     # 1.使用名词而不是动词
     - 不要使用:
         如:
         /getAllUsers    get          /users 、 get /users/002
         /createNewUser  post         /users
         /deleteAllUser  delete       /users 、delete /users/001
         /updateUser     put|patch    /users 、patch  /users/001
     ​
     # 2.Get方法和查询参数不应该涉及状态改变
     - 使用PUT,POST和DELETE方法 而不是GET方法来改变状态,不要使用GET进行状态改变
     ​
     # 3.使用复数名词
     - 不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数,如:
         /cars     而不是 /car
         /users    而不是 /user
         /products 而不是 /product
         /settings 而不是 /setting
         /orders   而不是 /order
             
     # 4. 使用子资源表达关系
     - 如果一个资源与另外一个资源有关系,使用子资源,如:
         GET /cars/711/drivers/  返回 car 711的所有司机
         GET /cars/711/drivers/4 返回 car 711的4号司机
         GET /users/11/pets      返回 user 11的所有宠物
         GET /users/11/pets/2    返回 user 11的2号宠物
     ​
     # 5.使用Http头声明序列化格式
     - 在客户端和服务端双方都要知道通讯的格式,格式在HTTP-Header中指定,如:
         Content-Type:定义请求格式
         Accept:定义系列可接受的响应格式
     ​
     # 6.为集合提供过滤排序选择和分页等功能
     - Filtering过滤:使用唯一的查询参数进行
         GET /cars?color=red 返回红色的cars
         GET /cars?seats<=2 返回小于两座位的cars集合
     ​
     - Sorting排序:允许针对多个字段排序
         GET /cars?sort=-manufactorer,+model
         这是返回根据生产者降序和模型升序排列的car集合
     ​
     - Field selection
         移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。
         GET /cars?fields=manufacturer,model,id,color
         
     - Paging分页
         使用limit和offset.实现分页,缺省时,limit=20和offset=0;
         GET /cars?offset=10&limit=5
         为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.
         链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定:
         Link: <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5>; 
         rel="next",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3>; 
         rel="last",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5>; 
         rel="first",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5>; 
         rel="prev",
     ​
     # 7.版本化你的API  支付宝  v1  v2   v3
     - 使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点,如:2.5.
       一般在Url后面使用?v,如:/blog/api/v1
     ​
     # 8. 使用Http状态码处理错误
     - 如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用
     - Http状态码提供70个出错,我们只要使用10个左右:
       `200 – OK – 一切正常
       `201 – OK – 新的资源已经成功创建
       `204 – OK – 资源已经成功删除
       `304 – Not Modified – 客户端使用缓存数据
       `400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效"
       `401 – Unauthorized – 请求需要用户验证
       `403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。
       `404 – Not found – 没有发现该资源
       `422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。
       `500 – Internal Server Error – API开发者应该避免这种错误。
     使用详细的错误包装错误:状态码、数据、header头信息
     {
       "errors": [
        {
         "userMessage": "Sorry, the requested resource does not exist",
         "internalMessage": "No car found in the database",
         "code": 34,
         "more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
        }
       ]
     }

    1.6 Rest API案例

    新建Spring Initializr、引入Spring Web的项目spring-boot-day8

    创建实体类User

     package com.study.entity;
     ​
     import java.util.Date;
     ​
     /**
      * @ClassName User
      * @Description TODO
      * @Author Jiangnan Cui
      * @Date 2022/6/29 16:13
      * @Version 1.0
      */
     public class User {
         private Integer id;
         private String name;
         private Double salary;
         private Date birthday;
     ​
         public User() {
         }
     ​
         public User(Integer id, String name, Double salary, Date birthday) {
             this.id = id;
             this.name = name;
             this.salary = salary;
             this.birthday = birthday;
         }
     ​
         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 Double getSalary() {
             return salary;
         }
     ​
         public void setSalary(Double salary) {
             this.salary = salary;
         }
     ​
         public Date getBirthday() {
             return birthday;
         }
     ​
         public void setBirthday(Date birthday) {
             this.birthday = birthday;
         }
     ​
         @Override
         public String toString() {
             return "User{" +
                     "id=" + id +
                     ", name='" + name + '\'' +
                     ", salary=" + salary +
                     ", birthday=" + birthday +
                     '}';
         }
     }
     ​

    创建实体类Pet

     package com.study.entity;
     ​
     /**
      * @ClassName Pet
      * @Description TODO
      * @Author Jiangnan Cui
      * @Date 2022/6/29 17:48
      * @Version 1.0
      */
     public class Pet {
         private Integer id;
         private String name;
         private Integer age;
     ​
         public Pet() {
         }
     ​
         public Pet(Integer id, String name, Integer age) {
             this.id = id;
             this.name = name;
             this.age = 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 "Pet{" +
                     "id=" + id +
                     ", name='" + name + '\'' +
                     ", age=" + age +
                     '}';
         }
     }

    创建控制器UserController

     package com.study.controller.v1;
     ​
     import com.study.entity.User;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.springframework.web.bind.annotation.*;
     ​
     import java.util.Date;
     ​
     /**
      * @ClassName UserController
      * @Description TODO
      * @Author Jiangnan Cui
      * @Date 2022/6/29 16:16
      * @Version 1.0
      */
     @RestController //专用于restful风格的注解=@Controller + @ResponseBody
                     //@Controller:专用于传统开发的注解
     @RequestMapping("/v1/users") //符合使用名词复数形式
     public class UserController {
         
         //新建日志对象
         private static final Logger logger = LoggerFactory.getLogger(UserController.class);
     ​
         /**
          * @MethodName findUserById
          * @Description 根据id查询某个用户的详细信息
          * @param: id
          * @return: com.study.entity.User
          * @Author Jiangnan Cui
          * @Date 16:28 2022/6/29
          */
         //@RequestMapping(value = "/{id}",method = RequestMethod.GET) 此种方式太繁琐,推荐使用下面这种
         @GetMapping("/{id}") //@RequestMapping的子类注解,使用GetMapping时只能使用GET方式访问当前请求
         //@ResponseBody //将控制器方法返回值转换为json格式
         //@PathVariable:在路径中获取请求参数
         public User findUserById(@PathVariable("id") Integer id){
             logger.info("本次id:{}",id);
             return new User(id,"小崔",1234.56,new Date());
         }
     }
     ​

    测试路径:http://localhost:8080/v1/users/1

     配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    安装Postman进行测试:Download Postman | Get Started for Free (访问官网后自动识别版本,下载后双击打开即安装完毕)

     package com.study.controller.v1;
     ​
     import com.study.entity.Pet;
     import com.study.entity.User;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.springframework.web.bind.annotation.*;
     ​
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Date;
     import java.util.List;
     ​
     /**
      * @ClassName UserController
      * @Description TODO
      * @Author Jiangnan Cui
      * @Date 2022/6/29 16:16
      * @Version 1.0
      */
     @RestController //专用于restful风格的注解=@Controller + @ResponseBody
                     //@Controller:专用于传统开发的注解
     @RequestMapping("/v1/users") //符合使用名词复数形式
     public class UserController {
         //新建日志对象
         private static final Logger logger = LoggerFactory.getLogger(UserController.class);
     ​
         /**
          * @MethodName findUserById
          * @Description 根据id查询某个用户的详细信息
          * @param: id
          * @return: com.study.entity.User
          * @Author Jiangnan Cui
          * @Date 16:28 2022/6/29
          */
         //@RequestMapping(value = "/{id}",method = RequestMethod.GET) 此种方式太繁琐,推荐使用下面这种
         @GetMapping("/{id}") //@RequestMapping的子类注解,使用GetMapping时只能使用GET方式访问当前请求
         //@ResponseBody //将控制器方法返回值转换为json格式
         //@PathVariable:在路径中获取请求参数
         public User findUserById(@PathVariable("id") Integer id){
             logger.info("本次id:{}",id);
             return new User(id,"小崔",1234.56,new Date());
         }
     ​
         /**
          * @MethodName findAllUser
          * @Description 查询所有用户信息
          * @return: java.util.List<com.study.entity.User>
          * @Author Jiangnan Cui
          * @Date 17:24 2022/6/29
          */
         @GetMapping
         //@ResponseBody
         public List<User> findAllUser(){
             ArrayList<User> users = new ArrayList<>();
             users.add(new User(1,"张三",1000.00,new Date()));
             users.add(new User(2,"李四",2000.00,new Date()));
             users.add(new User(3,"王五",3000.00,new Date()));
             return users;
         }
     ​
         /**
          * @MethodName addUser
          * @Description 新增用户信息
          * @param: user
          * @Author Jiangnan Cui
          * @Date 17:28 2022/6/29
          */
         @PostMapping
         //@ResponseBody //将方法的返回值转化为json格式,并响应请求
         //@RequestBody: 接收请求的json格式数据,将json格式数据转化为对象
         public void addUser(@RequestBody User user){
             logger.info("name:{},salary:{},birthday:{}",user.getName(),user.getSalary(),user.getBirthday());
             //调用业务方法
     ​
         }
     ​
         /**
          * @MethodName updateUser
          * @Description 更新用户信息
          * @param: user
          * @Author Jiangnan Cui
          * @Date 17:30 2022/6/29
          */
         @PutMapping("/{id}")
         //@ResponseBody
         public void updateUser(@PathVariable("id") Integer id,@RequestBody User user){
             logger.info("id:{},name:{},salary:{},birthday:{}",id,user.getName(),user.getSalary(),user.getBirthday());
             //调用业务方法
     ​
         }
     ​
         /**
          * @MethodName deleteUser
          * @Description 删除用户信息
          * @param: id
          * @Author Jiangnan Cui
          * @Date 17:32 2022/6/29
          */
         @DeleteMapping("/{id}")
         @ResponseBody
         public void deleteUser(@PathVariable("id") Integer id){
             logger.info("本次删除用户的id:{}",id);
         }
     ​
         /**
          * @MethodName findPetById
          * @Description 根据宠物id查询主人的一个宠物
          * @param: id
          * @param: petId
          * @return: com.study.entity.Pet
          * @Author Jiangnan Cui
          * @Date 17:54 2022/6/29
          */
         @GetMapping("/{id}/pets/{petId}")
         public Pet findPetById(@PathVariable("id") Integer id,@PathVariable("petId") Integer petId){
             logger.info("宠物主人的id:{}",id);
             logger.info("宠物id:{}",petId);
             return new Pet(1,"小灰灰",5);
         }
     ​
         /**
          * @MethodName findAllPet
          * @Description 查询主人的所有宠物
          * @param: id
          * @return: java.util.List<com.study.entity.Pet>
          * @Author Jiangnan Cui
          * @Date 17:57 2022/6/29
          */
         @GetMapping("/{id}/pets")
         public List<Pet> findAllPet(@PathVariable("id") Integer id){
             logger.info("宠物主人的id:{}",id);
             List<Pet> pets = Arrays.asList(
                     new Pet(1,"熊大",20),
                     new Pet(2,"熊二",10),
                     new Pet(3,"光头强",50)
             );
             return pets;
         }
     ​
     }
     ​

    测试路径: 

    (1)http://localhost:8080/v1/users

    (2)http://localhost:8080/v1/users

    (3)http://localhost:8080/v1/users/1

    (4)http://localhost:8080/v1/users/1

    (5)http://localhost:8080/v1/users/1/pets/1

    (6)http://localhost:8080/v1/users/1/pets

    RestFul标准版:利用RestFul响应类ResponseEntity和状态码HttpStatus优化UserController

    • ResponseEntity:SpringMVC封装的一个专用于RestFul的响应类,这个类在响应时可以提供响应的状态码,同时还可以自定义响应头信息。

    • HttpStatus:SpringMVC封装的一个枚举类型,这个类中都是网络中状态码

     package com.study.controller.v1;
     ​
     import com.study.entity.Pet;
     import com.study.entity.User;
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;
     import org.springframework.http.HttpStatus;
     import org.springframework.http.ResponseEntity;
     import org.springframework.web.bind.annotation.*;
     ​
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Date;
     import java.util.List;
     ​
     /**
      * @ClassName UserController
      * @Description TODO
      * @Author Jiangnan Cui
      * @Date 2022/6/29 16:16
      * @Version 1.0
      */
     @RestController //专用于restful风格的注解=@Controller + @ResponseBody
                     //@Controller:专用于传统开发的注解
     @RequestMapping("/v1/users") //符合使用名词复数形式
     public class UserController {
         //新建日志对象
         private static final Logger logger = LoggerFactory.getLogger(UserController.class);
     ​
         /**
          * 补充:
          * 1.ResponseEntity:SpringMVC封装的一个专用于RestFul的响应类,这个类在响应时可以提供响应的状态码,
          *   同时还可以自定义响应头信息。
          * 2.HttpStatus:SpringMVC封装的一个枚举类型,这个类中都是网络中状态码
          */
     ​
         /**
          * @MethodName findUserById
          * @Description 根据id查询某个用户的详细信息
          * @param: id
          * @return: com.study.entity.User
          * @Author Jiangnan Cui
          * @Date 16:28 2022/6/29
          */
         //@RequestMapping(value = "/{id}",method = RequestMethod.GET) 此种方式太繁琐,推荐使用下面这种
         @GetMapping("/{id}") //@RequestMapping的子类注解,使用GetMapping时只能使用GET方式访问当前请求
         //@ResponseBody //将控制器方法返回值转换为json格式
         //@PathVariable:在路径中获取请求参数
         public ResponseEntity<User> findUserById(@PathVariable("id") Integer id){
             logger.info("本次id:{}",id);
             User user = new User(id, "小崔", 1234.56, new Date());
             return new ResponseEntity<>(user, HttpStatus.OK);
         }
     ​
         /**
          * @MethodName findAllUser
          * @Description 查询所有用户信息
          * @return: java.util.List<com.study.entity.User>
          * @Author Jiangnan Cui
          * @Date 17:24 2022/6/29
          */
         @GetMapping
         //@ResponseBody
         public ResponseEntity<List<User>> findAllUser(){
             ArrayList<User> users = new ArrayList<>();
             users.add(new User(1,"张三",1000.00,new Date()));
             users.add(new User(2,"李四",2000.00,new Date()));
             users.add(new User(3,"王五",3000.00,new Date()));
             return new ResponseEntity<>(users,HttpStatus.OK);
         }
     ​
         /**
          * @MethodName addUser
          * @Description 新增用户信息
          * @param: user
          * @Author Jiangnan Cui
          * @Date 17:28 2022/6/29
          */
         @PostMapping
         //@ResponseBody //将方法的返回值转化为json格式,并响应请求
         //@RequestBody: 接收请求的json格式数据,将json格式数据转化为对象
         public ResponseEntity<Void> addUser(@RequestBody User user){
             logger.info("name:{},salary:{},birthday:{}",user.getName(),user.getSalary(),user.getBirthday());
             //调用业务方法
             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
         }
     ​
         /**
          * @MethodName updateUser
          * @Description 更新用户信息
          * @param: user
          * @Author Jiangnan Cui
          * @Date 17:30 2022/6/29
          */
         @PutMapping("/{id}")
         //@ResponseBody
         public ResponseEntity<Void> updateUser(@PathVariable("id") Integer id,@RequestBody User user){
             logger.info("id:{},name:{},salary:{},birthday:{}",id,user.getName(),user.getSalary(),user.getBirthday());
             //调用业务方法
             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
         }
     ​
         /**
          * @MethodName deleteUser
          * @Description 删除用户信息
          * @param: id
          * @Author Jiangnan Cui
          * @Date 17:32 2022/6/29
          */
         @DeleteMapping("/{id}")
         @ResponseBody
         public void deleteUser(@PathVariable("id") Integer id){
             logger.info("本次删除用户的id:{}",id);
         }
     ​
         /**
          * @MethodName findPetById
          * @Description 根据宠物id查询主人的一个宠物
          * @param: id
          * @param: petId
          * @return: com.study.entity.Pet
          * @Author Jiangnan Cui
          * @Date 17:54 2022/6/29
          */
         @GetMapping("/{id}/pets/{petId}")
         public ResponseEntity<Pet> findPetById(@PathVariable("id") Integer id,@PathVariable("petId") Integer petId){
             logger.info("宠物主人的id:{}",id);
             logger.info("宠物id:{}",petId);
             Pet pet = new Pet(1, "小灰灰", 5);
             return new ResponseEntity<>(pet,HttpStatus.OK);
         }
     ​
         /**
          * @MethodName findAllPet
          * @Description 查询主人的所有宠物
          * @param: id
          * @return: java.util.List<com.study.entity.Pet>
          * @Author Jiangnan Cui
          * @Date 17:57 2022/6/29
          */
         @GetMapping("/{id}/pets")
         public ResponseEntity<List<Pet>> findAllPet(@PathVariable("id") Integer id){
             logger.info("宠物主人的id:{}",id);
             List<Pet> pets = Arrays.asList(
                     new Pet(1,"熊大",20),
                     new Pet(2,"熊二",10),
                     new Pet(3,"光头强",50)
             );
             return new ResponseEntity<>(pets,HttpStatus.OK);
         }
     }
     ​

    再次启动服务,用Postman测试上面路径,从Postman中可以查看到相应状态码

    1.7 最终项目结构

    2. 异常处理

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    待完善 

  • 相关阅读:
    Yii2安装遇到Loading composer repositories with package information
    第七章 查找 八、B树
    SCI论文写作(二) | SCI论文的引言(Introduction)部分
    JAVASE零基础到高级教程(1)------ 集成开发环境安装使用
    【C++】类型转换
    uni-app_01
    HTTP缓存小结
    Java面试八股之Java中==和equals()的区别
    Python接口自动化测试之post请求详解
    一文带你了解.NET能做什么?
  • 原文地址:https://blog.csdn.net/xiaocui1995/article/details/125527530