【业务功能篇48】后端接口开发的统一规范

news2024/10/6 8:30:39

业务背景:日常工作中,我们开发接口时,一般都会涉及到参数校验、异常处理、封装结果返回等处理。而我们项目有时为了快速迭代,在这方面上有所疏忽,后续导致代码维护比较难,不同的开发人员的不同习惯,各式各样的接口返回,所以专门进行了一次业务整改,统一规范专项

如果每个后端开发在参数校验、异常处理等都是各写各的,没有统一处理的话,代码就不优雅,也不容易维护。所以,作为一名合格的后端开发工程师,我们需要统一校验参数,统一异常处理、统一结果返回,让代码更加规范、可读性更强、更容易维护。

接口统一响应对象返回

作为后端开发,我们项目的响应结果,需要统一标准的返回格式。一般一个标准的响应报文对象,有一下几个属性

  • code :响应状态码
  • message :响应结果描述
  • data:返回的数据

1)响应状态码一般用枚举表示:

包结构: com.xxx.common.core.domain.ResultCode

/**
 * 响应状态码
 **/
public enum ResultCode {

    /**操作成功**/
    SUCCESS("200","操作成功"),

    /**操作失败**/
    ERROR("500","操作失败"),;

    /**
     * 自定义状态码
     **/
    private String code;
    
    
    /**自定义描述**/
    private String message;

    ResultCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

2)因为返回的数据类型不是确定的,我们可以使用泛型,如下:

包结构:com.xxx.common.core.domain.BaseResponse

/**
 * 响应结果封装对象
 **/
public class BaseResponse<T> implements Serializable {

    private static final long serialVersionUID = 1901152752394073986L;

    /**
     * 响应状态码
     */
    private String code;


    /**
     * 响应结果描述
     */
    private String message;

    /**
     * 返回的数据
     */
    private T data;


    /**
     * 成功返回
     * @param data
     * @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>
     */
    public static <T> BaseResponse<T> success(T data){

        BaseResponse<T> response = new BaseResponse<>();
        response.setCode(ResultCode.SUCCESS.getCode());
        response.setMessage(ResultCode.SUCCESS.getMessage());
        response.setData(data);

        return response;
    }


    /**
     * 失败返回
     * @param message
     * @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>
     */
    public static <T> BaseResponse<T> fail(String message){

        BaseResponse<T> response = new BaseResponse<>();
        response.setCode(ResultCode.ERROR.getCode());
        response.setMessage(message);

        return response;
    }

    /**
     * 失败返回
     * @param message
     * @return: com.msb.hjycommunity.common.core.domain.BaseResponse<T>
     */
    public static <T> BaseResponse<T> fail(String code, String message){

        BaseResponse<T> response = new BaseResponse<>();
        response.setCode(code);
        response.setMessage(message);

        return response;
    }


    public String getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

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

3)测试

  • 创建user实体类
public class User {

    private String userId;

    private String username;
    
    public User() {
    
    }
    
    public User(String userId, String username) {
        this.userId = userId;
        this.username = username;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

 

  • 创建UserController
@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/queryUserById")
    public BaseResponse<User> queryUserById(String userId){

        if(userId != null){
           return BaseResponse.success(new User(userId,"spike"));
        }else{
            return BaseResponse.fail("查询用户信息失败!");
        }
    }
}

 

  • 测试
http://localhost:8888/user/queryUserById?userId=1

//output
{
  "code": "200",
  "message": "操作成功",
  "data": {
    "userId": "1",
    "username": "tom"
  }
}

http://localhost:8888/user/queryUserById

//output
{
  "code": "500",
  "message": "查询用户信息失败!",
  "data": null
}

使用注解,统一参数校验

我们在实际的开发过程中经常会遇到需要对参数进行校验的情况,比如在需要用户输入手机号的时候他是不是真的输入了一个合法的手机号,在需要用户输入一个邮箱的时候他是不是真的输入了一个合法的邮箱,用户输入的内容是不是超出了长度限制等等。

当一个表单的数据较多的时候,单纯的数据校验代码就会占到很大的幅度,所以简化基础数据的校验可以省去我们很多的工作,让我们能更专注于功能的实现。

接下来我们一起看一下 springboot中参数校验(validation)的使用,首先导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Validator可以非常方便的制定校验规则,并自动帮你完成校验。首先在入参里需要校验的字段加上注解,每个注解对应不同的校验规则,并可制定校验失败后的信息:


public class User {
    
    private String userId;

    @NotNull(message = "username 不能为空")
    private String username;
}

 

校验规则和错误提示信息配置完毕后,接下来只需要在接口需要校验的参数上加上@Validated注解,并添加BindResult参数即可方便完成验证:


@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/addUser")
    public BaseResponse addUser(@Validated User user, BindingResult bindingResult){

        List<FieldError> fieldErrors = bindingResult.getFieldErrors();

        //如果参数校验失败,会将错误信息封装成对象组装在 BindingResult
        if(!fieldErrors.isEmpty()){
            return BaseResponse.fail(fieldErrors.get(0).getDefaultMessage());
        }

        return BaseResponse.success("OK");
    }
}

 

测试

http://localhost:8888/user/addUser?username="tom"

{
  "code": "500",
  "message": "userId 不能为空",
  "data": null
}

http://localhost:8888/user/addUser?username="tom"&userId=1

{
  "code": "200",
  "message": "操作成功",
  "data": "OK"
}

内置的校验注解有很多,罗列如下:

统一异常处理

日常开发中,我们一般都是自定义统一的异常类

  • 自定义异常可以携带更多的信息。
  • 项目开发中经常是很多人负责不同的模块,使用自定义异常可以统一了对外异常展示的方式。
  • 自定义异常语义更加清晰明了,一看就知道是项目中手动抛出的异常。

自定义异常类, 所在包结构: com.xxx.common.core.exception.BaseException


/**
 * 基础异常
 **/
public class BaseException extends RuntimeException{


    /**
     * 错误码
     */
    private String code;


    /**
     * 错误消息
     */
    private String defaultMessage;

    public BaseException() {
    }

    public BaseException(String code, String defaultMessage) {
        this.code = code;
        this.defaultMessage = defaultMessage;
    }

    public String getCode() {
        return code;
    }

    public String getDefaultMessage() {
        return defaultMessage;
    }
}

 

在controller 层,我们来使用这个自定义异常:


@RequestMapping("/queryUser")
public BaseResponse queryUser(User user){

    //模拟查询失败抛出异常
    throw new BaseException("500","测试异常类!");
}

 

这块代码,没什么问题,但是如果在很多地方都抛出这个异常,我们处理起来就会比较麻烦。

这时我们可以借助注解@RestControllerAdvice,让代码更优雅。@RestControllerAdvice是一个应用于Controller层的切面注解,它一般配合@ExceptionHandler注解一起使用,作为项目的全局异常处理。示例代码如下:


/**
 * 全局异常处理器
 **/
@RestControllerAdvice
public class GlobalExceptionHandler {


}

我们有想要拦截的异常类型,比如想拦截BaseException类型,就新增一个方法,使用@ExceptionHandler注解修饰,并指定你想处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理! 


@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 基础异常
     * @param e
     * @return: com.xxx.common.core.domain.BaseResponse
     */
    @ExceptionHandler(BaseException.class)
    @ResponseBody
    public BaseResponse baseExceptionHandler(BaseException e){
        
        return BaseResponse.fail(e.getDefaultMessage());
    }

}

 

测试

http://localhost:8888/hejiayun/user/queryUser

{
  "code": "500",
  "message": "测试异常类!",
  "data": null
}

总结:整改的几个维度,但这里需要注意的是,全局异常设计,在个人看来并不一定是有效的,有时会封装过简单,还不易于排查代码问题。

  • 通过Validation 完成了方便的参数校验
  • 通过全局异常处理 + 自定义异常完成了异常操作的规范
  • 通过数据统一响应完成了响应数据的规范
  • 多个方面组装非常优雅的完成了后端接口的协调,让开发人员有更多的经历注重业务逻辑代码,轻松构建后端接口。

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

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

相关文章

5、Java入门教程【数组】

数组是用于存储同种类型的多个数据的容器。 一、声明 //语法 dataType[] arrayRefVar; // 首选的方法 或 dataType arrayRefVar[]; // 效果相同&#xff0c;但不是首选方法//示例 double[] myList; // 首选的方法 或 double myList[]; // 效果相同&…

服务机器人应用

随着时代的发展&#xff0c;机器人技术在各个领域越来越普及。在服务领域&#xff0c;服务机器人的应用也越来越受到人们的欢迎。服务机器人将会在商业、医疗、教育、酒店等领域得到应用&#xff0c;并成为未来发展的趋势。 在商业领域中&#xff0c;服务机器人可以承担很多工作…

分类预测 | MATLAB实现LSTM(长短期记忆神经网络)分类预测

分类效果 基本介绍 长短期记忆网络Q通常被称为LSTM,是一种特殊的RNN,能够学习长期依赖性。由Hochreiter和Schmidhuber(1997)提出的,并且在接下来的工作中被许多人改进和推广。 LSTM在各种各样的问题上表现非常出色,现在被广泛使用。LSTM被明确设计用来避免长期依赖性问…

vue3基础+进阶(一、Vue3项目创建并相比vue2熟悉项目结构)

目录 第一章、认识create-vue 1.1 简介 1.2 使用create-vue创建项目 1.2.1 创建步骤以及注意事项 1.2.2 注意事项 1.2.3 熟悉项目&#xff0c;与vue2区分 第一章、认识create-vue 1.1 简介 create-vue是Vue官网新的脚手架工具&#xff0c;底层切换到了vite(下一代前端工…

苍穹外卖-【day03】-【公共字段自动填充】-【新增】-【分页】-【删除】-【修改】

苍穹外卖-day03 课程内容 公共字段自动填充新增菜品菜品分页查询删除菜品修改菜品 **功能实现&#xff1a;**菜品管理 菜品管理效果图&#xff1a; 1. 公共字段自动填充 1.1 问题分析 在上一章节我们已经完成了后台系统的员工管理功能和菜品分类功能的开发&#xff0c;在…

SeLa:Self Labeling Via Simultaneous Clustering and Representation Learning

方法论 有监督的分类任务实现 给定一个将数据I映射至特征向量 x ∈ R D x ∈ R^D x∈RD 的深度神经网络 Φ Φ Φ &#xff0c;即 x Φ ( I ) x Φ ( I ) xΦ(I)。使用有N个样本 I 1 , … , I N I_1 , … , I_N I1​,…,IN​, 且具有标签 y 1 , … , y N ∈ { 1 , … , K }…

线性DP———最长公共子序列问题(LCS)

LCS问题 求两序列具有相同元素的最长子序列&#xff0c;我们可以用到动态规划的方法来解决问题 我们用 来表示序列 与序列 能组成的LCS的长度&#xff0c;的状态转移方程如下&#xff1a; 使用两层for循环就可以解决此问题&#xff0c;时间复杂度为,可以处理n<7000左右…

pycharm里debug时torch数组显示不全

pycharm里查看torch数组全部值 一、在Pycharm运行torch数组时&#xff0c;通常只能看到数组的一部分二、解决办法1、debug后&#xff0c;鼠标右键想要查看完整的数组&#xff0c;选择Evaluate Expression2、输入np.array(x0.data)&#xff0c;x0为想要查看的数组名&#xff0c;…

C#中的抽象类(abstract)

C#中的抽象类&#xff08;abstract&#xff09;。 1&#xff1a;抽象方法只作声明&#xff0c;而不包含实现&#xff0c;可以看成是没有实现体的虚方法 2&#xff1a;抽象类不能被实例化 3&#xff1a;抽象类可以但不是必须有抽象属性和抽象方法&#xff0c;但是一旦有了抽象方…

HTML5学习简记(更新中~)

HTML定义 HTML是一种超文本标记语言&#xff0c;超文本即我们在网页中看到的链接&#xff0c;标记指的是语言中的标签 标签 标签一般成对出现&#xff0c;结束标签比开始标签多一个/ 此外标签一般分为单标签与双标签 HTML基本骨架 其中,HTML标签代表整个网页&#xff0c;hea…

ubuntu迁移anaconda到另外的目录,完美解决问题

背景&#xff1a;anaconda放在/home/下&#xff0c;但是太大了&#xff0c;需要迁移到/data/&#xff0c;找了很多资料&#xff0c;都没解决问题。 一、你可以先做一下尝试&#xff1a; ①拷贝anaconda到你想要的新目录 cp -r /home/anaconda3 /data/ ②将原来的anaconda备…

我国开设大数据相关专业的大学有哪些?

社会对大数据相关专业的人才有着巨大的需求&#xff0c;据预测&#xff0c;到2030年&#xff0c;大数据及相关产业规模达到上万亿元&#xff0c;大数据产业将成为重要的经济增长点。数据是企业最宝贵的资源&#xff0c;大数据分析技术在提升企业经营管理、提高销售业绩、降低管…

微服务组件Sentinel

什么是Sentinel Sentinel 是一种用于流量控制、熔断降级和系统负载保护的开源框架。它由阿里巴巴集团开发并开源&#xff0c;旨在帮助开发人员构建可靠和稳定的分布式系统。 Sentinel 提供了以下主要功能&#xff1a; 流量控制&#xff1a;Sentinel 可以通过限制请求的速率或…

怎么学习Java I/O相关的知识和技术? - 易智编译EaseEditing

要学习Java I/O&#xff08;输入输出&#xff09;相关的知识和技术&#xff0c;可以按照以下步骤进行&#xff1a; 理解Java I/O基础知识&#xff1a; 首先&#xff0c;了解Java I/O的基本概念和术语&#xff0c;包括输入流和输出流、字节流和字符流、文件读写等。掌握Java中…

1.12 springboot 整合log4j打印日志

1.除去springboot自带的日志 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><!-- 除去springboot自带的日志 --><exclusion><groupId>org.sprin…

vxe 行编辑自动加载行级别的下级下拉框数据

效果&#xff1a;进入编辑的时候自动根据ID带出下级下拉框 实现思路&#xff1a;行编辑进入之前&#xff0c;调用加载下拉框的方法 activeRowMethod({ row, rowIndex }) {this.getSpecsList(row.wlid);return ret; }, :edit-config"{trigger: click,mode: row,autoClear…

LT8619B 是一款HDMI转TTL或者2 PORT LVDS的芯片。

LT8619B 1. 概述 LT8619B是龙迅基于清除边缘技术的高性能HDMI接收芯片&#xff0c;符合HDMI 1.4&#xff08;高清多媒体接口&#xff09;规范。RGB 输出端口可支持 RGB888/RGB666/RGB565 格式&#xff0c;输出分辨率最高可支持 4Kx2K 分辨率。凭借可编程标量&#xff0c;LT86…

Python爬虫之Scrapy框架系列(23)——分布式爬虫scrapy_redis浅实战【XXTop250部分爬取】

目录&#xff1a; 1.实战讲解&#xff08;XXTop250完整信息的爬取&#xff09;&#xff1a;1.1 使用之前做的完整的XXTOP250项目&#xff0c;但是设置为只爬取一页&#xff08;共25个电影&#xff09;,便于观察1.2 配置settings文件中使用scrapy_redis的必要配置&#xff0c;并…

git -- SSL certificate problem

SSL certificate problem 1.问题描述 新建一个仓库&#xff0c;在向里面上传文件时&#xff0c;出现SSL证书问题 2.解决方法 这个问题是由于没有配置信任的服务器HTTPS验证。默认&#xff0c;cURL被设为不信任任何CAs&#xff0c;就是说&#xff0c;它不信任任何服务器验证。…

概率论的学习和整理--番外12:一个经典dubo模型的概率计算等

目录 1 经典模型知识科普 1.1 知识来源 1.2 下面是摘取的部分规则 2 这个经典dubo的概率和期望 2.1 网上计算的概率&#xff0c;期望全是负&#xff0c;赌徒悲剧 2.2 为什么会这样呢 3 假设把下注庄家不抽水&#xff0c;获得100%收益而不是95&#xff0c;多少次后可以赢…