Spring Boot REST API - CRUD 操作

news2024/11/27 14:34:43

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
    @PostConstruct
    public 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 match
    public 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 size
        if (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
    @ExceptionHandler
    public ResponseEntity<StudentErrorResponse> handleException(StudentNotFoundException exec) {
        // create a studentErrorResponse
        StudentErrorResponse error = new StudentErrorResponse();
        error.setStatus(HttpStatus.NOT_FOUND.value());
        error.setMessage(exec.getMessage());
        error.setTimeStamp(System.currentTimeMillis());

        // return ResponseEntity
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

显示结果如下:

在这里插入图片描述

添加 generic 报错处理

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

在这里插入图片描述

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

    @ExceptionHandler
    public ResponseEntity<StudentErrorResponse> handleException(Exception e) {
        // create a studentErrorResponse
        StudentErrorResponse error = new StudentErrorResponse();
        error.setStatus(HttpStatus.BAD_REQUEST.value());
        error.setMessage(e.getMessage());
        error.setTimeStamp(System.currentTimeMillis());

        // return ResponseEntity
        return 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.coloradmin.cn/o/1587314.html

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

相关文章

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示汉字的功能

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示汉字的功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍LCD1602字符型液晶显示器介绍一、LCD1602字符型…

20240411,内存分区模型

一&#xff0c;内存分区模型 生成可执行文件EXE文件之前代码区【函数体的二进制代码——共享-对于被频繁执行的程序&#xff0c;只需要在内存中有一份代码&#xff1f;&#xff0c;只读】&#xff0c;全局区【全局变量&#xff0c;静态变量&#xff0c;常量&#xff0c;该区域…

012:vue结合纯CSS实现蛇形流程图/步骤条

文章目录 1. 实现效果2. 实现代码 1. 实现效果 2. 实现代码 <template><div class"container"><div v-for"(item, index) in list" class"grid-item"><div class"step">step{{index1}}</div></div&…

计算机网络知识等汇总补充

计算机网络知识汇总补充 一、四次挥手1、为什么TCP要等待2MSL2、如果说一个系统中&#xff0c;有大量的time_wait和close_wait&#xff0c;会是什么原因&#xff1f; 二、你是怎么解决粘包问题&#xff1f;三、你觉得哪些场景适合redis四、redis的持久化策略五、你会怎么保证my…

视频基础学习六——视频编码基础三(h264框架配合图文+具体抓包分析 万字)

系列文章目录 视频基础学习一——色立体、三原色以及像素 视频基础学习二——图像深度与格式&#xff08;RGB与YUV&#xff09; 视频基础学习三——视频帧率、码率与分辨率 视频基础学习四——视频编码基础一&#xff08;冗余信息&#xff09; 视频基础学习五——视频编码基础…

WinRAR再爆0 day漏洞,0 day漏洞该如何有效预防

WinRAR再爆0 day漏洞&#xff0c;已被利用超过4个月。 Winrar是一款免费的主流压缩文件解压软件&#xff0c;支持绝大部分压缩文件格式的解压&#xff0c;全球用户量超过5亿。Group-IB研究人员在分析DarkMe恶意软件时发现WinRAR在处理ZIP文件格式时的一个漏洞&#xff0c;漏洞…

基于Springboot的笔记记录分享网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的笔记记录分享网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

牛客 NC36 在两个长度相等的排序数组中找到上中位数【中等 模拟 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/6fbe70f3a51d44fa9395cfc49694404f 思路 直接模拟2个数组有顺序放到一个数组中&#xff0c;然后返回中间的数参考答案java import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 pu…

使用htmlentities()和nl2br()将文本数据正确显示到前台

问题&#xff1a; 在后台textarea里编辑了有一串字符串&#xff0c;虽然在textarea里编辑是有换行效果的&#xff0c;但是数据获取到就只是\n&#xff0c;前端是不认识这个的&#xff0c;正确输出到前台的换行只能是<br/>。 $str "ABCDEFGHIJKLMNOPQ"; echo…

Multisim仿真二极管、晶体管和场效应管学习笔记

Multisim仿真二极管、晶体管和场效应管 &#xff08;note&#xff1a;使用Multisim14.0版本进行仿真&#xff09; 文章目录 Multisim仿真二极管、晶体管和场效应管二极管的I-V特性晶体管的I-V特性场效应管的I-V特性 二极管的I-V特性 插入I-V analyzer 原理图绘制 改变仿真…

【MCU开发规范】:MCU的性能测试

MCU的性能测试 前序性能评判方法MIPSCoreMark EEMBC其他参考 前序 我们平时做MCU开发时&#xff0c;前期硬件选型&#xff08;选那颗MCU&#xff09;基本由硬件工程师和架构决定&#xff0c;到软件开发时只是被动的开发一些具体功能&#xff0c;因此很少参与MCU的选型。 大部分…

面试经典算法系列之二叉树1 -- 从前序与中序遍历序列构造二叉树

面试经典算法16 - 从前序与中序遍历序列构造二叉树 LeetCode.105 公众号&#xff1a;阿Q技术站 问题描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根…

02 - Git 之命令 +

1 Git相关概念 1.1 以下所谈三个区&#xff0c;文件并不只是简单地在三个区转移&#xff0c;而是以复制副本的方式转移 使用 Git 管理的项目&#xff0c;拥有三个区域&#xff0c;分别是 Working area工作区&#xff08;亦称为 工作树Working Tree&#xff09;、stage area …

学习JavaEE的日子 Day33 File类,IO流

Day33 1.File类 File是文件和目录路径名的抽象表示 File类的对象可以表示文件&#xff1a;C:\Users\Desktop\hhy.txt File类的对象可以表示目录路径名&#xff1a;C:\Users\Desktop File只关注文件本身的信息&#xff08;文件名、是否可读、是否可写…&#xff09;&#xff0c…

简单了解JVM

一.JVM简介 jvm及Java virtual machineJava虚拟机&#xff0c;它是一个虚构出来的计算机&#xff0c;一种规范。其实抛开这么专业的句子不说&#xff0c;就知道 JVM 其实就类似于一台小电脑运行在 windows 或者 linux 这些操作系统环境下即可。它直接和操作系统进行交互&#…

无人新零售引领的创新浪潮

无人新零售引领的创新浪潮 在数字化时代加速演进的背景下&#xff0c;无人新零售作为商业领域的一股新兴力量&#xff0c;正以其独特的高效性和便捷性重塑着传统的购物模式&#xff0c;开辟了一条充满创新潜力的发展道路。 依托人脸识别、物联网等尖端技术&#xff0c;无人新…

中位数和众数-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第49讲。 中位数和众数&…

初识 QT

初始QT 什么是QTQT发展史QT支持的平台QT的优点QT的应用场景搭建QT开发环境QT的开发工具概述QT下载安装 使用QT创建项目QT 实现Hello World程序使用按钮控件来实现使用标签控件来实现 项目文件解析widget.hmain.cppwidget.cppwidget.ui.pro文件 对象树QT 窗口坐标体系 什么是QT …

《战神4》和《战神5》有什么联系吗 苹果电脑如何运行《战神4》苹果电脑玩战神 Mac玩游戏 战神5攻略 crossover激活码

《战神4》&#xff08;God of War 2018&#xff09;和《战神5》&#xff08;God of War: Ragnark&#xff09;是一对引人注目的游戏作品&#xff0c;它们不仅在游戏界引起了广泛的关注&#xff0c;也给玩家带来了深入探索北欧神话世界的机会。这两部游戏之间的联系不仅体现在剧…

hbase基础shell用法

HBase中用create命令创建表&#xff0c;具体如下&#xff1a; create student,Sname,Ssex,Sage,Sdept,course 此时&#xff0c;即创建了一个“student”表&#xff0c;属性有&#xff1a;Sname,Ssex,Sage,Sdept,course。因为HBase的表中会有一个系统默认的属性作为行键&#x…