6.2 构建 RESTful 应用接口

news2024/12/28 5:27:46

第6章 构建 RESTful 服务

6.1 RESTful 简介
6.2 构建 RESTful 应用接口
6.3 使用 Swagger 生成 Web API 文档
6.4 实战:实现 Web API 版本控制

6.2 构建 RESTful 应用接口

6.2.1 Spring Boot 对 RESTful 的支持

Spring Boot 提供的spring-boot-starter-web组件完全支持开发 RESTful API,提供了与 REST 操作方式(GET、POST、PUT、DELETE)对应的注解:

  1. @GetMapping:处理 GET 请求,获取资源。
  2. @PostMapping:处理 POST 请求,新增资源。
  3. @PutMapping:处理 PUT 请求,更新资源。
  4. @DeleteMapping:处理 DELETE 请求,删除资源。
  5. @PatchMapping:处理 PATCH 请求,用于部分更新资源。

通过这些注解就可以在 Spring Boot 项目中轻松构建 RESTful 接口。其中比较常用的是 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping 四个注解。

使用 Spring Boot 开发 RESTful 接口非常简单,通过 @RestController 定义控制器,然后使用 @GetMapping 和 @PostMapping 等注解定义地址映射,实现相应的资源操作方法即可。

示例:

RESTfulController.java

package com.example.restfulproject.controller;

import com.example.restfulproject.model.User;
import org.springframework.web.bind.annotation.*;

/**
 * RESTful 接口简单案例(增删改查)
 */
@RestController
public class RESTfulController {

    @GetMapping(value = "/user/{id}")
    public String getUserById(@PathVariable String id) {
        return "getUserById:" + id;
    }

    @PostMapping(value = "/user")
    public String save(@RequestBody User user) {
        String name = user.getName();
        return "save successed";
    }

    @PutMapping(value = "/user")
    public String update(@RequestBody User user) {
        return "update successed";
    }

    @DeleteMapping(value = "/user/{id}")
    public String delete(@PathVariable String id) {
        return "delete id:" + id;
    }


/*
    // 上面的注解是 @RequestMapping 注解的简化:

    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
    public String getUserById(@PathVariable String id) {
        return "getUserById:" + id;
    }

    @RequestMapping(value = "/user", method = RequestMethod.POST)
    public String save(User user) {
        return "save successed";
    }

    @RequestMapping(value = "/user", method = RequestMethod.PUT)
    public String update(User user) {
        return "update successed";
    }

    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable String id) {
        return "delete id:" + id;
    }
*/
}

6.2.2 Spring Boot 实现 RESTful API

1. 设计API

在 RESTful 架构中,每个网址代表一种资源,所以 URI 中建议不要包含动词,只包含名词即可,而且所用的名词往往与数据库的表名对应。

(1)接口定义

下表是用户管理模块的接口定义,实际项目的 RESTful API 文档要更详细,还会定义全部请求的数据结构体。

用户管理模块API说明

HTTP Method接口地址接口说明
POST/user创建用户
GET/user/id根据id获取用户信息
PUT/user更新用户
DELETE/user/id根据id删除对应的用户

上表中定义了用户管理模块的接口,根据 REST 的定义,我们将用户定义为一种资源,通过 POST、DELETE、PUT、GET 等 HTTP Method 实现对用户的增、删、改、查。

(2)状态码和提示信息定义

除了设计 URL 接口之外,还需要定义服务端向客户端返回的状态码和提示信息。详细状态码说明见下表。

用户管理模块状态码说明:

状态码状态说明
200Ok,请求成功
201Created,新增成功
203Updated,修改成功
204Deleted,删除成功

除了定义用户管理相关的业务状态码之外,还需要定义通用的错误码,如 400 对应数据校验错误、401 对应数据无权限等。

用户管理模块错误码说明:

错误码错误说明
400数据验证错误
401无权限
404资源不存在
500服务端错误

2. 实现用户管理接口

UserManagementController.java

package com.example.restfulproject.controller;

import com.example.restfulproject.comm.utils.JSONResult;
import com.example.restfulproject.model.User;
import org.springframework.web.bind.annotation.*;

/**
 * Spring Boot 实现 RESTful API
 */
@RestController
@RequestMapping("/userManagement")
public class UserManagementController {
    /**
     * 用户新增
     */
    @PostMapping(value = "/user")
    public JSONResult save(@RequestBody User user) {
        System.out.println("用户创建成功:" + user.getName());
        return JSONResult.ok(201, "用户创建成功");
    }

    /**
     * 用户修改
     */
    @PutMapping(value = "/user")
    public JSONResult update(@RequestBody User user) {
        System.out.println("用户修改成功:" + user.getName());
        return JSONResult.ok(203, "用户修改成功");
    }

    /**
     * 用户删除
     */
    @DeleteMapping(value = "/user/{userId}")
    public JSONResult delete(@PathVariable String userId) {
        System.out.println("用户删除成功:" + userId);
        return JSONResult.ok(204, "用户删除成功");
    }

    /**
     * 获取用户
     */
    @GetMapping(value = "/user/{userId}")
    public JSONResult queryUserById(@PathVariable String userId) {
        User user = new User();
        user.setId(userId);
        user.setName("zhangsan");
        user.setAge(20);
        System.out.println("获取用户成功:" + userId);
        return JSONResult.ok(200, "获取用户成功", user);
    }
    
}

JSONResult.java

package com.example.restfulproject.comm.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;

/**
 * 定义数据处理类
 *
 * @Title:: JSONResult.java
 * @Package com.example.webproject.comm.utils
 * @Description: 自定义响应数据结构
 *          200: 表示成功
 *          500: 表示错误,错误信息在 msg 字段中
 *          501: bean 验证错误,无论多少个错误都以 map 形式返回
 *          502: 拦截器拦截到用户 token 出错
 *          555: 异常抛出信息
 * Copyright: Copyright (c) 2023
 *
 * @create: 2023-02-05 17:35:54
 * @Modify: 2023-02-17 19:41:30
 * @version V1.1
 */
public class JSONResult {
    // 定义 jackson 对象 (关于jackson的使用详解请参考:https://juejin.cn/post/6844904166809157639)
    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 响应业务状态
    private Integer code;
    // 响应消息
    private String msg;
    // 响应中的数据
    private Object data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public JSONResult() {
    }

    public JSONResult(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public JSONResult(Object data) {
        this.code = 200;
        this.msg = "OK";
        this.data = data;
    }

//    // 是否成功
//    public Boolean isSuccess() {
//        return this.code == 200;
//    }

    // 构建返回的数据
    public static JSONResult build(Integer status, String msg, Object data) {
        return new JSONResult(status, msg, data);
    }

    // 200:成功,有返回值
    public static JSONResult ok(Object data) {
        return new JSONResult(data);
    }

    // 200:成功,没有返回值
    public static JSONResult ok() {
        return new JSONResult(null);
    }

    //200:成功,没有返回值
    public static JSONResult ok(Integer code, String msg) {
        return new JSONResult(code, msg, null);
    }

    // 200: 成功
    public static JSONResult ok(Integer code, String msg, Object data) {
        return new JSONResult(code, msg, data);
    }

    //500:错误,错误信息在 msg 字段中
    public static JSONResult errorMsg(String msg) {
        return new JSONResult(500, msg, null);
    }

    //501:bean 验证错误,无论多少个错误都以 map 形式返回
    public static JSONResult errorMap(Object data) {
        return new JSONResult(501, "error", data);
    }

    //502:拦截器拦截到用户 token 出错
    public static JSONResult errorTokenMsg(String msg) {
        return new JSONResult(502, msg, null);
    }

    //555:异常抛出信息
    public static JSONResult errorException(String msg) {
        return new JSONResult(555, msg, null);
    }

    /**
     * @Description: 将 json 结果集转化为 JSONResult 对象,需要转换的对象是一个类
     *
     * @param jsonData
     * @param clazz
     * @return
     */
    public static JSONResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, JSONResult.class);
            }

            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("code").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * @Description: 没有 object 对象的转化
     *
     * @param json
     * @return
     */
    public static JSONResult format(String json) {
        try {
            return MAPPER.readValue(json, JSONResult.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Description: Object 是集合转化,需要转换的对象是一个list
     *
     * @param jsonData
     * @param clazz
     * @return
     */
    public static JSONResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if(data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory().constructCollectionType(List.class,clazz));
            }
            return build(jsonNode.get("code").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

}

3. 验证测试

(1)获取用户:http://localhost:8080/userManagement/user/2001
获取用户.png

(2)用户新增:http://localhost:8080/userManagement/user
用户新增.png

(3)用户修改:http://localhost:8080/userManagement/user
用户修改.png

(4)用户删除:http://localhost:8080/userManagement/user/2001
用户删除.png

来源:《Spring Boot 从入门到实战》学习笔记

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

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

相关文章

Pygame中画圆

在Pygame中&#xff0c;可以通过draw模块下的circle()函数来进行画圆。1 准备工作的完成在画圆之前需要导入Pygame模块、初始化Pygame模块以及创建Surface对象。import pygame from pygame.locals import * pygame.init() screen pygame.display.set_mode((600,500))其中&…

SpringBoot实现 内置 定时 发送邮件功能

前段时间因为公司用了定时任务&#xff0c;所以写了2篇定时任务的文章&#xff0c;一篇是正常如何在Springboot 编程中如何去使用quartz &#xff0c;第二篇就是 正常业务性的增删改查&#xff0c;今天我们来看下如何使用 quartz 去定时给女朋友发邮件 &#xff0c;结尾会放上完…

智能电子办公标牌解决方案

一、WiFi智能电子标牌 智能电子办公标牌将它放在任何地方&#xff0c;以可视化会议日程、约会信息、行动计划和协作任务&#xff0c;使团队能够更有效地工作并更好地利用空间。 优势&#xff1a; ● 超低功耗&#xff0c;充一次电管用一年&#xff0c;支持Type-C接口充电 ●…

Linux之进程

一.冯诺依曼体系 在计算机中&#xff0c;CPU&#xff08;中央处理器&#xff09;是不直接跟外部设备直接进行通信的&#xff0c;因为CPU处理速度太快了&#xff0c;而设备的数据读取和输入有太慢&#xff0c;而是CPU以及外设直接跟存储器&#xff08;内存&#xff09;打交道&am…

Python 之 Matplotlib 柱状图(竖直柱状图和水平柱状图)、直方图和饼状图

文章目录一、柱状图二、竖直柱状图1. 基本的柱状图2. 同位置多柱状图3. 堆叠柱状图三、水平柱状图1. 基本的柱状图2. 同位置多柱状图3. 堆叠柱状图四、直方图 plt.hist()1. 返回值2. 添加折线直方图3. 不等距分组4. 多类型直方图5. 堆叠直方图五、饼状图 pie()1. 百分比显示 pe…

初步使用MSYS2

在此镜像站点下载&#xff0c; https://mirror.tuna.tsinghua.edu.cn/help/msys2/ 根据资料&#xff0c; MSYS2 &#xff08;Minimal SYStem 2&#xff09; 是一个MSYS的独立改写版本&#xff0c;主要用于 shell 命令行开发环境。同时它也是一个在Cygwin &#xff08;POSIX …

FPGA 10M50DCF672C7G/10M50DCF672C8G/10M50DCF672I7G工业、汽车和消费应用

FPGA现场可编程门阵列 10M50DCF672C7G/10M50DCF672C8G/10M50DCF672I7G 封装FBGA672FBGA672封装图&#xff08;明佳达电子&#xff09;描述MAX 10器件是单芯片、非易失性低成本可编程逻辑器件(pld)&#xff0c;用于集成最优的系统组件集。MAX 10设备的亮点包括:内部存储双配置闪…

Spring Data JPA 中 CrudRepository 和 JpaRepository 的区别

1 问题描述Spring Data JPA 中&#xff0c;CrudRepository 和 JpaRepository 有何区别&#xff1f;当我在网上找例子的时候&#xff0c;发现它们可以互相替换使用。它们有什么不同呢&#xff1f;为什么你习惯用其中的一个而不是另一个呢&#xff1f;2 CrudRepository 和 JpaRep…

ArcGIS网络分析之发布网络分析服务(二)

在上一篇中讲述了如何构建网络分析数据集,本篇将讲解如何发布网络分析服务。本文将使用上一篇中建立的网络数据集,下载地址在上一篇博文的最后已给出。 之前我们已经实现了基于ArcMap中的网络分析,但是仅仅支持本地是万万不够的,这里我们的目的就是将我们建好的网络分析图…

【OJ】两个圆

&#x1f4da;Description: 直角坐标系内现有两个半径相等的圆&#xff0c;问两圆的位置关系。 位置关系有&#xff1a;重合&#xff0c;相切&#xff0c;相离&#xff0c;相交&#xff1b; 若两圆相交&#xff0c;需要求出两圆的重叠面积。 ⏳Input: 输入包含多组数据&a…

【项目精选】户籍管理系统(视频+论文+源码)

点击下载源码 当今社会人们生活质量越来越高&#xff0c;人们对生活品质的追求不断提升&#xff0c;对于孩子求学&#xff0c;变更住所等情况时有发生&#xff0c;因此对于户籍变动管理就显得十分重要&#xff0c;管理用户的户籍信息可以有效防止信息错乱&#xff0c;信息管理过…

百度沈抖:文心一言将通过百度智能云对外提供服务

2月17日&#xff0c;在2023 AI工业互联网高峰论坛上&#xff0c;百度智能云宣布“文心一言”将通过百度智能云对外提供服务&#xff0c;为产业带来AI普惠。 百度集团执行副总裁、百度智能云事业群总裁沈抖表示&#xff0c;“文心一言”是基于百度智能云技术打造出来的大模型&a…

A Time Series is Worth 64 Words(PatchTST模型)论文解读

摘要 我们提出了一种高效的基于Transformer设计的模型&#xff0c;用于多变量时间序列预测和自我监督表征学习&#xff08;self-supervised learning&#xff09;。它基于两个关键部分&#xff1a;1、将时间序列分隔成子序列级别的patches&#xff0c;作为Transformer的输入&a…

Allegro负片层不显示反盘的原因和解决办法

Allegro负片层不显示反盘的原因和解决办法 在用Allegro做PCB设计的时候,负片设计是较为常用的一种方式,有时会出现打开负片层却看不到反盘的情况,如下图 L2层是负片层 L2层仍然只能看到盘 如何才能看到反盘显示的效果,具体操作如下 首先确定L2层层叠里面设置的是负片

java ssm爱宠宠物医院挂号预约系统管理系统设计与实现

本课题所实现的宠物医院网站是基于网页&#xff0c;它可以实现网上预约挂号&#xff0c;评价等基本功能。用户只要手边有一部手机或者一台电脑&#xff0c;可以上网浏览网页&#xff0c;便可以使用本系统&#xff0c;没有时间和地点的限制&#xff0c;使得就医预约&#xff0c;…

枚举类(enum)

定义&#xff1a;在某些情况下&#xff0c;一个类的实例对象是有限且固定的&#xff0c;可将该类称为“枚举类”。枚举类是JDK 1.5 之后提出来的。例如&#xff1a;四季只有春夏秋冬4个季节&#xff0c;性别只有男女2个&#xff0c;故四季类和性别类均可称为“枚举类”。 在自…

python(8.5)--列表习题

目录 一、求输出结果题 二、计算列表元素个数 三、查找是否存在某元素 四、删除某元素 五、如何在列表中插入元素 六、如何从列表中删除重复的元素 七、 如何将列表中的元素按照从小到大的顺序排序 八、从列表中删除重复的元素 九、大到小的顺序排序 一、求输出结…

【源码解析】Ribbon和Feign实现不同服务不同的配置

Ribbon服务实现不同服务&#xff0c;不同配置是通过RibbonClient和RibbonClients两个注解来实现的。RibbonClient注册的某个Client配置类。RibbonClients注册的全局默认配置类。 Feign实现不同服务&#xff0c;不同配置&#xff0c;是根据FeignClient来获取自定义的配置。 示…

【大数据】Apache Spark 3.3.0 正式发布,新特性详解

简介 Apache Spark 3.3.0 从2021年07月03日正式开发&#xff0c;历时近一年&#xff0c;终于在2022年06月16日正式发布&#xff0c;在 Databricks Runtime 11.0 也同步发布。这个版本一共解决了 1600 个 ISSUE&#xff0c;感谢 Apache Spark 社区为 Spark 3.3 版本做出的宝贵贡…

Redis学习【8】之Redis RDB持久化

文章目录Redis 持久化1 持久化基本原理2 RDB(Redis DataBase) 持久化2.1 持久化的执行2.2 手动 save 命令2.3 手动 bgsave 命令2.4 自动条件触发2.5 查看持久化时间3 RDB 优化配置3.1 save3.2 stop-write-on-bgsave-error3.3 rdbcompression3.4 rdbchecksum3.5 sanitize-dump-p…