[spring] Spring Boot REST API - CRUD 操作

news2024/11/26 4:30:54

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/1604488.html

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

相关文章

IO引脚服用和映射

什么是端口复用 STM32F4 有很多的内置外设&#xff0c;这些外设的外部引脚都是与 GPIO 复用的。也就是说&#xff0c;一个 GPIO如果可以复用为内置外设的功能引脚&#xff0c;那么当这个 GPIO 作为内置外设使用的时候&#xff0c;就叫做复用。在芯片数据手册或STM32F4XX参考手…

光纤和铜缆:了解不同通信媒介的优势

在现代通信技术中&#xff0c;光纤和铜缆是两种主要的数据传输媒介。它们各有优势和局限性&#xff0c;但都在我们的日常生活中扮演着不可或缺的角色。 左侧&#xff08;网络跳线&#xff09;右侧&#xff08;光纤跳线&#xff09; 一、光纤的原理与优势 ADOP光纤跳线 光纤通信…

Day38: 动态规划 LeedCode:509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 蓝桥杯: 更小的数

对于动态规划问题&#xff0c;拆解为如下五步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化(容易数组溢出)确定遍历顺序举例推导dp数组 509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐…

STM32 HAL库F103系列之ADC实验(1)

ADC工作原理&#xff1a; 1、输入通道&#xff1a; 2、转换序列&#xff1a; A/D转换被组织为两组&#xff1a;规则组&#xff08;常规转换组&#xff09;和注入组&#xff08;注入转换组&#xff09; 规则组最多可以有16个转换&#xff0c;注入组最多有4个转换 规则组和注入…

艾迪比皮具携手工博科技SAP ERP公有云,打造数字化转型新标杆

4月1日&#xff0c;广州市艾迪比皮具有限公司&#xff08;以下简称“艾迪比”&#xff09;SAP S/4HANA Cloud Public Edition&#xff08;以下简称“SAP ERP公有云”&#xff09;项目正式启动。双方项目组领导、成员出席本次项目启动会&#xff0c;为未来项目的顺利实施打下坚实…

Computer Organization/Architecture 计算机组织/架构/结构 重要观念和笔记(陆续更新中,2024/04/17周三,已更新)

前情提要&#xff1a;我的说法比较白话&#xff0c;希望可以更好理解其中一些观念&#xff0c;这篇会以中文为主&#xff0c;专有名词还是用英文&#xff0c;好吧应该会中英穿插&#xff0c;自己学的时候感觉听中文会吸收比较快&#xff0c;也可能是我英文比较烂的关系&#xf…

软件产品许可证书 Licence 全流程研发(使用非对称加密技术,既安全又简单)

本篇博客对应的代码地址&#xff1a; Gitee 仓库地址&#xff1a;https://gitee.com/biandanLoveyou/licence 源代码百度网盘链接: https://pan.baidu.com/s/1_ZhdcENcrk2ZuL11hWDLTQ?pwdbmxi 提取码: bmxi 1、背景介绍 公司是做软件 SAAS 服务的&#xff0c;一般来说软件部…

《操作系统导论》第26章读书笔记:并发:介绍

《操作系统导论》第26章读书笔记&#xff1a;并发&#xff1a;介绍 —— 杭州 2024-04-18 夜 文章目录 《操作系统导论》第26章读书笔记&#xff1a;并发&#xff1a;介绍0.前言1.实例&#xff1a;线程创建(略)2.为什么更糟糕&#xff1a;共享数据(略)3.核心问题&#xff1a;不…

基于Springboot+Vue的Java项目-企业客户管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

uiautomation、pytest、schedule实现桌面程序自动化(初级)02

一&#xff1a;安装uiAutomation 前置条件:安装python、pycharm 命令行安装 Pip install uiautomation2.0.17 #指定版本 二&#xff1a;安装辅助工具&#xff1a;inspect.exe和、Accessibility Insights For Windows定位元素工具 辅助工具介绍 步骤中提到…

【每天一个linux小知识】tailf 和 tail -f

目录 典型使用场景区别 典型使用场景 如果我们实时查看文件的末尾内容&#xff0c;特别是当文件持续写入时&#xff0c;可以使用tailf 和 tail -f。两者都可以显示文件的最后几行内容&#xff0c;并且在文件被追加新内容时&#xff0c;会实时显示这些新内容。 tailf演示 区别 …

逆向案例二十八——红某点集登录接口逆向序

网址&#xff1a;aHR0cHM6Ly93d3cuaHJkanl1bi5jb20vIy9sb2dpbj9yZWRpcmVjdD0lMkZyZWFsVGltZUxpdmluZw 登录接口&#xff0c;发现两个参数加密&#xff0c;分别是pwd和sig,t很明显是时间戳。 观察pwd,发现很像md5加密&#xff0c;我输入的密码是123456&#xff0c;在在线加密网…

mac安装nvm详细教程

0. 前提 清除电脑上原有的node (没有装过的可以忽略)1、首先查看电脑上是否安装的有node,查看node版本node -v2、如果有node就彻底删除nodesudo rm -rf /usr/local/{bin/{node,npm},lib/node_modules/npm,lib/node,share/man/*/node.*}2、保证自己的电脑上有安装git,不然下载n…

Python基于Django的旅游城市关键词分析和提取,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt 小区物业管理系统 的设计与实现

一.项目介绍 系统分为管理员 和 业主 两块&#xff1a; 管理员点击进入到系统操作界面&#xff0c;可以对首页、业主信息管理、管理员信息管理、 楼栋和房屋信息管理、物业费管理、地下停车位管理、公告信息管理、报修信息管理、 投诉管理以及个人信息等功能模块 …

数字逻辑课程实验环境配置与使用说明

文章目录 I.虚拟机搭建1.1 Vmware安装1.2 Win XP安装1.3 xftp7安装 I. Quartus II安装II. 使用说明2.1 新建工程2.2 在工程中加入代码2.3 代码编译波形仿真 I.虚拟机搭建 1.1 Vmware安装 Vmware17安装教程 1.2 Win XP安装 Win XP安装教程 1.3 xftp7安装 给虚拟机添加FTP …

部署wordpress

查看别名type ll ll 是 ls -l --colorauto 的别名 设置别名alias alias ymyum install -y 使用别名ym nginx 取消别名unalias ym 基于LNMP做一个wordpress nginx mysql 5.7 PHP 7.4 1、linux基本环境 修改主机名 hostnamectl set-hostname $name 关闭防火墙及selinux …

python爬豆瓣top250电影

文章目录 前言分析与实现1.对豆瓣网网站进行Ajax分析2.发送请求3.进一步筛选&#xff08;提取&#xff09; 完整代码 前言 通过这个项目&#xff0c;可以让小白对爬虫有一个初步认识&#xff0c;爬取豆瓣top250是一个初学者学爬虫的必经之路&#xff0c;话不多说&#xff0c;我…

【缓存常见问题】

在使用缓存时特别是在高并发场景下会遇到很多问题&#xff0c;常用的问题有缓存穿透、缓存击穿、缓存雪崩以及缓存一致性问题。 1、缓存穿透 首先&#xff0c;什么是缓存穿透呢&#xff1f; 缓存穿透是指请求一个不存在的数据&#xff0c;缓存层和数据库层都没有这个数据&…

图片各种格式区别介绍:

图片各种格式区别介绍&#xff1a; JPEG格式&#xff08;Joint Photographic Experts Group&#xff09; JPEG格式一种有损压缩格式&#xff0c;能够将图像压缩在很小的储存空间&#xff0c;图像中重复或不重要的资料会被丢失&#xff0c;因此容易造成图像数据的损伤。尤其是…