[spring] Spring Boot REST API - CRUD 操作

Spring Boot REST API - CRUD 操作

这里主要提一下 spring boot 创建 rest api,并对其进行 CRUD 操作

jackson & gson

目前浏览器和服务端主流的交互方式是使用 JSON(JavaScript Object Notation),但是 JSON 没有办法直接和 Java 的 POJO 创建对应关系,因此就需要一些库去实现这个转换的功能:

  • 将 JSON 转换成 Java POJO
  • 将 Java POJO 转化成 JSON
  • 实现序列化和反序列化

目前比较主流的两个库是 jackson 和 gson,这里选用 jackson,不需要做任何的配置,spring 默认使用 jackson,并且在默认情况下使用 setter/getter 对 POJO 的属性进行赋值

POM

项目依旧使用 spring initializer 创建,这里是额外需要勾选的两个库:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>

其中 lombok 可选,我只是懒得写 boilerplate code 所以直接安装了 lombok,配制方法在 Intellij 安装配置 lombok,这里不多赘述。如果 IDE 没有配置 lombok 可能会导致这个工具没法用

创建一个 rest controller

实现如下:

package com.example.demo.rest;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/test")
public class DemoRestController {// add code for the "/hello" endpoint@GetMapping("/hello")public String sayHello() {return "Hello World";}
}

效果如下:

在这里插入图片描述

这里几个注解的用途如下:

  • @RestController 告知 spring boot 这是一个 restful api 的 controller

    是传统 spring mvc 里 @Controller + @ResponseBody 的结合

  • @RequestMapping

    这个注解 spring mvc 里就有,表示处理的所有 rest api 都会 map 到 /test 这个路径下

  • @GetMapping

    表示这里会接受一个 HTTP 的 Get 请求,对应的路径是 /hello

    比较新版本的 sping mvc 也应该有这个注解

POJO

这里就是非常简单的定义一个 java class:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private String firstName;private String lastName;
}

其中三个注解来自于 lombok

Rest Controller 实现

CRUD 的实现,关于具体的 API 设计结尾处会稍微提一下

获取全部学生

实现如下:

@RestController
@RequestMapping("/api")
public class StudentRestController {// define endpoint for "/students" - return a list of students@GetMapping("/students")public List<Student> getStudents() {List<Student> students = new ArrayList<>();students.add(new Student("Peter", "Parker"));students.add(new Student("Stephen", "Strange"));students.add(new Student("Steve", "Rodgers"));return students;}
}

在这里插入图片描述

这里没有连接数据库,所以用一个 ArrayList 放所有的对象,并进行返回。可以看到返回值是一个正常的 JSON

路径变量 Path Variables

path variable 是一种可以从 URL 路径中获取变量的方式,如 client 可以调用这个路径: /api/students/{studentId}, 那么 studentId 就是路径变量

简单重构

开始之前先做一下简单重构,这样可以不用反复创建新的 ArrayList:

public class StudentRestController {private List<Student> students;// define @PostConstruct to load the student data, it will only load data once@PostConstructpublic void loadData() {this.students = new ArrayList<>();students.add(new Student("Peter", "Parker"));students.add(new Student("Stephen", "Strange"));students.add(new Student("Steve", "Rodgers"));}// define endpoint for "/students" - return a list of students@GetMapping("/students")public List<Student> getStudents() {return students;}}

@PostConstruct 是 JavaEE 的规范之一,会在容器初始化后当前 bean 后被调用,且只会被调用一次,因此这里用来实现数据的赋值

路径变量实现

实现比较粗暴,直接获取对应下标的值:

    // define ent point for "students/{studentId}" - return student at index@GetMapping("/students/{studentId}")// by default, param should matchpublic Student getStudent(@PathVariable int studentId) {return this.students.get(studentId);}

实现效果如下:

在这里插入图片描述

⚠️:函数中的变量名和路径变量中的名称应当保持一致

异常处理

假设 studentId 并不是一个合法的参数,如 ArrayList 中只有三条数据,但是提供的 id 为 99,或者提供的不是数字,而是字符串,那么就会出现对应的异常:

在这里插入图片描述

这种情况下,用户其实并不需要了解这么多的信息,ta 可能只需要知道传过去的 id 不对,数据库找不到对应的数据即可。spring mvc 也提供了一个 @ExceptionHandler 去处理报错信息。实现方法如下:

  1. 创建对应的 error response POJO
  2. 创建对应的 exception 类
  3. 更新对应的 rest 实现,抛出在第 2 步里实现的 exception
  4. 使用 @ExceptionHandler 捕获对应异常,并且返回一个对应的 ResponseEntity<T>, 其中 T 为第 1 步里创建的 POJO,jackson 会将其转换成对应的 JSON 对象

定义 error res pojo

实现如下,非常简单:


@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentErrorResponse {private String message;private int status;private long timeStamp;
}

依旧使用 Lombok 解决大部分的问题

创建 custom exception

这里实现的是 not found exception,因为没有用默认参数,也没有用全参,所以没有使用 Lombok


public class StudentNotFoundException extends RuntimeException {public StudentNotFoundException(String message) {super(message);}public StudentNotFoundException(String message, Throwable cause) {super(message, cause);}public StudentNotFoundException(Throwable cause) {super(cause);}
}

抛出异常

    // define ent point for "students/{studentId}" - return student at index@GetMapping("/students/{studentId}")public Student getStudent(@PathVariable int studentId) {// check the studentId against list sizeif (studentId >= this.students.size() || studentId < 0) {throw new StudentNotFoundException(("Student id not found - " + studentId));}return this.students.get(studentId);}

这里主要处理的是 index out of bound 的异常,如果参数类型不对则需要 overload 方法:

捕获异常

使用 ExceptionHandler 去捕获对应的异常,并且将 error code 修改成 404,表示无法根据当前 id 获取对应数据

    // add the exception handler@ExceptionHandlerpublic ResponseEntity<StudentErrorResponse> handleException(StudentNotFoundException exec) {// create a studentErrorResponseStudentErrorResponse error = new StudentErrorResponse();error.setStatus(HttpStatus.NOT_FOUND.value());error.setMessage(exec.getMessage());error.setTimeStamp(System.currentTimeMillis());// return ResponseEntityreturn new ResponseEntity<>(error, HttpStatus.NOT_FOUND);}

显示结果如下:

在这里插入图片描述

添加 generic 报错处理

这个时候如果传入 string 的话,抛出的异常还是不太好看:

在这里插入图片描述

所以这里可以添加一个 generic 的报错信息,表示传进来的参数不对,是 bad request 即可:

    @ExceptionHandlerpublic ResponseEntity<StudentErrorResponse> handleException(Exception e) {// create a studentErrorResponseStudentErrorResponse error = new StudentErrorResponse();error.setStatus(HttpStatus.BAD_REQUEST.value());error.setMessage(e.getMessage());error.setTimeStamp(System.currentTimeMillis());// return ResponseEntityreturn new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);}

显示结果如下:

在这里插入图片描述

注意这里所有的处理都是在 controller 中实现的:

在这里插入图片描述

全局异常处理

这里会使用 @ControllerAdvice 这个注解去实现,这是一个 AOP 的具体实现——即向已经存在的代码中注入新的行为(advice)

这里实现的方式很简单

  1. 创建一个新的 exception handler class,添加 @ControllerAdvice 注解

    @ControllerAdvice
    public class StudentRestExceptionHandler {}
    
  2. 重构

    将 controller 中的 exception handling 删掉

    同时将 exception handling 贴到 StudentRestExceptionHandler 中去

实现后的结构如下:

在这里插入图片描述

这样这个 handler 就能捕捉全局的报错,如修改一下 DemoRestController 中的代码,使其同样报错,也是可以捕获到的:

在这里插入图片描述

API 设计

写一些比较常识性的内容,已经对 RESTful 有了解的可以跳过

设计 API 的时候主要需要考虑三个点:

  • 谁会使用这个 API

    这个主要看的是目标用户,如这个 API 是会在同一个项目使用?公司内部使用?还是公开项目?

  • API 将会被怎样使用

    也就是说 API 的使用情况,如交易系统需要考虑付款、退款(部分退款/全部退款)、查看付款状态

    目前来说主流是 RESTful,不过使用 GraphQL 又是不同的情况

  • 任务需求是什么

    resource 的名称,支持的 HTTP 请求头等

目前来说主流的 API 设计规范如下:

HTTP MethodEndpointCRUD 操作
POST/employeeCreate
GET/employee/Read
GET/employee/{id}Read
PUT/employee/{id}Update
DELETE/employee/{id}Delete

之前看到一些比较反常识的操作有一个: /api/deleteEmployee,如果是 delete 的话,应该是 HTTP 使用 DELETE,Endpoint 用 employees

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/816801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python-numpy(3)-线性代数

一、方程求解 参考资料 对于Ax b 这种方程&#xff1a; np.linalg.inv(A).dot(B)np.linalg.solve(A,b) 1.1 求解多元一次方程一个直观的例子 # AXB # X A^(-1)*B A np.array([[7, 3, 0, 1], [0, 1, 0, -1], [1, 0, 6, -3], [1, 1, -1, -1]]) B np.array([8, 6, -3, 1]…

cannot import name ‘get_host‘ from ‘urllib3.util.url‘

Error in py_module_import(module, convert convert) : ImportError: cannot import name get_host from urllib3.util.url (D:\\url.py) Run reticulate::py_last_error() for details. 这个错误表明在 urllib3 模块的 util.url 子模块中找不到名为 get_host 的函数。这可能…

第十五届蓝桥杯省赛C/C++大学B组真题及赛后总结

目录 个人总结 C/C 组真题 握手问题 小球反弹 好数 R 格式 宝石组合 数字接龙 爬山 拔河 ​编辑 再总结及后续规划 个人总结 第一次参加蓝桥杯&#xff0c;大二&#xff0c;以前都在在学技术&#xff0c;没有系统的学过算法。所以&#xff0c;还是花了挺多时间去备…

Rust - 所有权

所有的程序都必须和计算机内存打交道&#xff0c;如何从内存中申请空间来存放程序的运行内容&#xff0c;如何在不需要的时候释放这些空间&#xff0c;成了重中之重&#xff0c;也是所有编程语言设计的难点之一。在计算机语言不断演变过程中&#xff0c;出现了三种流派&#xf…

基于深度学习的花卉检测系统(含PyQt界面)

基于深度学习的花卉检测系统&#xff08;含PyQt界面&#xff09; 前言一、数据集1.1 数据集介绍1.2 数据预处理 二、模型搭建三、训练与测试3.1 模型训练3.2 模型测试 四、PyQt界面实现参考资料 前言 本项目是基于swin_transformer深度学习网络模型的花卉检测系统&#xff0c;…

软考125-上午题-【软件工程】-传统软件的测试策略

一、传统软件的测试策略 有效的软件测试实际上分为4步进行&#xff0c;即&#xff1a;单元测试、集成测试、确认测试、系统测试。 1-1、单元测试&#xff08;模块测试&#xff09; 单元测试也称为模块测试&#xff0c;在模块编写完成且无编译错误后就可以进行。 单元测试侧重…

温故知新之-TCP Keepalive机制及长短连接

[学习记录] 前言 TCP连接一旦建立&#xff0c;只要连接双方不主动 close &#xff0c;连接就会一直保持。但建立连接的双方并不是一直都存在数据交互&#xff0c;所以在实际使用中会存在两种情况&#xff1a;一种是每次使用完&#xff0c;主动close&#xff0c;即短连接&…

JVM虚拟机(五)强引用、软引用、弱引用、虚引用

目录 一、强引用二、软引用三、弱引用四、虚引用五、总结 引文&#xff1a; 在 Java 中一共存在 4 种引用&#xff1a;强、软、弱、虚。它们主要指的是&#xff0c;在进行垃圾回收的时候&#xff0c;对于不同的引用垃圾回收的情况是不一样的。下面我们就一起来看一下这 4 种引用…

51单片机实验03-单片机定时/计数器实验

目录 一、实验目的 二、实验说明 1、51单片机有两个16位内部计数器/定时器&#xff08;C/T&#xff0c; Counter/Timer&#xff09;。 2、模式寄存器TMOD 1) M1M0工作模式控制位&#xff1b; 2) C/T定时器或计数器选择位&#xff1a; 3&#xff09;GATE定时器/计数器运行…

软考系规第2章思维导图,软硬件网络和次新技术大杂烩

虽然目前系统规划与管理师的教程是否改版存在不确定性&#xff0c;但是不影响咱们先概要了解当前的教程&#xff0c;使用思维导图的方式粗读教程。 为了帮助你更好的学习系规教程&#xff0c;降低系规教程阅读门槛&#xff0c;指尖疯特发起了教程伴读活动&#xff0c;通过伴读脑…

关于GDAL计算图像坐标的几个问题

关于GDAL计算图像坐标的几个问题_gdal读取菱形四角点坐标-CSDN博客 这篇文章写的很好&#xff0c;讲清楚了图像行列号与图像点坐标&#xff08;x,y&#xff09;对应关系&#xff0c;以及图像行列号如何转为地理坐标的&#xff0c;转载一下做个备份。 1.关于GDAL计算图像坐标的…

部署Kafka集群图文详细步骤

1 集群规划 共三台虚拟机同处overlay网段&#xff0c;每台虚拟机部署一套kafka和zookeeper&#xff0c;kafka_manager安装其中一台虚拟机上即可。 HostnameIP addrPortListenerzk1docker-swarm分配2183:2181zk2docker-swarm分配2184:2181zk3docker-swarm分配2185:2181k1docke…

python-使用bottle时间简易服务器

python-使用bottle时间简易服务器 背景调试读取文本所有内容字段解释json字符串解析追加写入文件 整理后整理后写入文件方法将目录下所有文本的内容批量追加到一个文本搜索字符串方法实现简易服务器通过浏览器访问 背景 202310.txt内容是一段json字符串&#xff0c;目的是通过…

C++进阶技巧:如何在同一对象中存储左值或右值

如何在同一对象中存储左值或右值 一、背景二、跟踪值2.1、存储引用2.2、存储值 三、存储variant四、通用存储类4.1、定义const访问4.2、定义非const访问 五、创建存储六、总结 一、背景 C 代码似乎经常出现一个问题&#xff1a;如果该值可以来自左值或右值&#xff0c;则对象如…

Arrow, 一个六边形的 Python 时间库

文章目录 Arrow, 一个六边形的 Python 时间库第一部分&#xff1a;背景介绍第二部分&#xff1a;库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第…

代码学习记录42---动态规划

随想录日记part42 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.04.14 主要内容&#xff1a;今天开始要学习动态规划的相关知识了&#xff0c;今天的内容主要涉及&#xff1a;最长递增子序列 &#xff1b;最长连续递增序列 &#xff1b;最长重复子数组 ;最长公…

关于部署ELK和EFLK的相关知识

文章目录 一、ELK日志分析系统1、ELK简介1.2 ElasticSearch1.3 Logstash1.4 Kibana&#xff08;展示数据可视化界面&#xff09;1.5 Filebeat 2、使用ELK的原因3、完整日志系统的基本特征4、ELK的工作原理 二、部署ELK日志分析系统1、服务器配置2、关闭防火墙3、ELK ElasticSea…

最优算法100例之48-链表中倒数第k个结点

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 链表中倒数第k个结点 题解报告 ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {if(pListHead == NULL)ret…

Go语言入门|包、关键字和标识符

目录 Go语言 包文件 规则 关键字 规则 标识符 规则 预定义标识符 Go语言 Go语言是一种静态类型、编译型和并发型的编程语言&#xff0c;由Google开发。Go的源代码文件以.go为扩展名&#xff0c;文件名通常与包名保持一致。一个Go文件可以包含多个顶级声明&#xff0c;…

访问者模式类图与代码

某图书管理系统中管理着两种类型的文献&#xff1a;图书和论文。现在要求统计所有馆藏文献的总页码(假设图书馆中有一本540页的图书和两篇各25页的论文&#xff0c;那么馆藏文献的总页码就是590页)。采用Visitor(访问者)模式实现该要求&#xff0c;得到如图7.16所示的类图。 访…