首页 > 其他分享 >Springboot学习第二天

Springboot学习第二天

时间:2024-01-22 21:23:12浏览次数:27  
标签:return Springboot ApiResult 接口 学习 第二天 异常 public 请求

今天的学习内容是如何在项目中设计统一响应接口返回值,达到统一的格式

1. 响应实体

我们首先要定义一个公共的接口响应实体,以后所有的接口返回值,都是返回的这个公共响应实体。

这样做的好处是可以统一返回值的风格,编译接口的维护。

需要包含3个关键的成员变量:

  1. 状态码
  2. 返回信息
  3. 数据

例如下面这样的:api请求响应实体

@AllArgsConstructor
@Data
public class ApiResult<T> {

/** * 请求成功状态码 */ public static final int OK = HttpStatus.HTTP_OK; /** * 接口返回码 */ private int code; /** * 接口返回信息 */ private String message; /** * 数据 */ private T data; }

这样用户请求接口之后,接口返回的数据结构都是一样的。

2. 响应实体工具类

为了更加优雅的创建相应实体类,我们可以增加一个专门的工具类。

这个工具类的职责是创建上面的ApiResult类的实例。

当然有两种情况:

  1. 成功
  2. 失败

我们处理的时候可以区分开: api请求响应实体处理工具类


public class ApiResultUtil {

    private ApiResultUtil() {
    }

    /**
     * 请求成功
     *
     * @param data 数据
     * @param <T>  数据类型
     * @return 接口相应实体
     */
    public static <T> ApiResult<T> success(T data) {
        return new ApiResult<>(ApiResult.OK, null, data);
    }

    /**
     * 请求成功
     *
     * @param <T> 数据类型
     * @return 接口相应实体
     */
    public static <T> ApiResult<T> success() {
        return success(null);
    }

    /**
     * 请求成功
     *
     * @param code    返回码
     * @param message 返回信息
     * @param <T>     数据类型
     * @return 接口相应实体
     */
    public static <T> ApiResult<T> error(int code, String message) {
        return new ApiResult<>(code, message, null);
    }
}

ApiResultUtil工具类中包含了两个重载的success方法,主要是处理接口请求成功的情况。

而error方法,主要是为了处理接口请求出现异常的情况。

需要注意的是ApiResultUtil类有一个私有的无参构造方法,是为了防止调用者new这个类的实例对象的一种常规做法,很多JDK源码中都有类似的做法。

3. 业务异常

有了上面公共的响应实体类,我们可以先处理异常了。

但异常有两种:

  1. 系统异常
  2. 业务异常

系统异常我们在统一处理时,错误码都返回500没问题。

但如果有些业务异常,错误码都返回500,这种设计不太合理。

因此,我们需要增加一个专门的业务异常类:BusinessException。

AllArgsConstructor
@Data
public class BusinessException extends RuntimeException {

    public static final long serialVersionUID = -6735897190745766939L;

    /**
     * 异常码
     */
    private int code;

    /**
     * 具体异常信息
     */
    private String message;

    public BusinessException() {
        super();
    }
}

这个异常类继承了RuntimeException类,是一种运行时异常,后面好处理。

包含了两个成员变量:

  1. code:表示异常码
  2. message:表示异常信息

比如用户添加接口中,出现用户名称相同时,异常信息可以提示:用户名称重复。

4. 全局异常处理

接下来,我们可以统一处理全局异常了。

在Spring MVC中可以通过@RestControllerAdvice注解处理全局异常:

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 统一处理异常
     *
     * @param e 异常
     * @return API请求响应实体
     */
    @ExceptionHandler(Throwable.class)
    public ApiResult handleException(Throwable e) {
        if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            log.info("请求出现业务异常:", e);
            return ApiResultUtil.error(businessException.getCode(), businessException.getMessage());
        }
        log.error("请求出现系统异常:", e);
        return ApiResultUtil.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部错误");
    }
}

创建了GlobalExceptionHandler类打上了@RestControllerAdvice注解,并且在handleException方法上打上了@ExceptionHandler注解,处理的异常是最底层的Throwable。

然后在该方法中需要判断一下异常类型,如果是后续的业务代码中抛出的业务异常,则使用业务异常的返回码和返回信息。

否则使用ApiResultUtil.error方法统一转换成服务器内部错误异常。

5. 正常接口响应处理

处理好全局异常之后,接下来,处理接口正常响应的情况,也需要把结果做封装。

在Spring MVC可以通过@ControllerAdvice注解实现这个功能。

@ControllerAdvice
public class GlobalApiResultHandler implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        String requestURI = request.getRequestURI();
        return matchUrl(requestURI);
    }

    private boolean matchUrl(String uri) {
        if (StringUtils.isBlank(uri)) {
            return false;
        }
        return uri.contains("/v1");
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ApiResult) {
            return (ApiResult) body;
        }
        return ApiResultUtil.success(body);
    }
}

GlobalApiResultHandler类打上了@ControllerAdvice注解,并且实现了ResponseBodyAdvice接口。

重写了该接口的supports和beforeBodyWrite方法。

supports方法决定了beforeBodyWrite方法是否执行,只有supports方法返回true时,beforeBodyWrite方法才会执行。

在beforeBodyWrite方法中,将接口的返回值使用ApiResultUtil.success方法做封装,当然如果返回值已经是ApiResult类型,则直接返回无需重复封装。

在supports方法中,目前是根据uri中的请求路径做的判断,防止把一些html或者其他类型的页面请求也做封装了。

6 测试

 

接下来,测试一下接口的统一封装功能。

先看看接口请求成功的情况。

还是访问swagger地址:http://localhost:8011/swagger-ui/index.html

请求查询用户接口:

返回值:

 可以看到接口返回值已经按照我们的预期返回了code、message、data的数据结构。

再看看接口请求异常的情况。

请求添加用户接口:

username叫string的用户名在数据库中已存在,调用接口之后返回:

我们可以看到全局异常处理也成功了。

后面需要修改添加用户的接口代码,如果出现用户名称已存在是,正常情况下是跑的业务异常,不应该是提示:用户服务器内部错误,而应该是:用户名称重复。

好了,这个是项目实战中的异常处理专题,这套异常的设计,在实际的工作中还是比较有借鉴意义的。

 本学习项目来自苏三商城,该项目是知识星球:java突击队 的内部项目

 

 

 

 

 

 

 

标签:return,Springboot,ApiResult,接口,学习,第二天,异常,public,请求
From: https://www.cnblogs.com/cmx2012/p/17981105

相关文章

  • 在 SpringBoot 项目中使用 Mybatis 打印 SQL 日志
    前言我们在项目中使用的持久层框架大部分都是mybatis,如果在日志中能打印sql的话,对于我们排查问题会更加方便。第一种方式:修改mybatis配置修改配置mybatis:configuration:log-impl:org.apache.ibatis.logging.slf4j.Slf4jImpllogging:level:com.imooc.p......
  • 操作系统学习
    操作系统是什么是一种软件,提供给用户和其他上层软件方便的接口和环境!!!系统资源的管理者提供处理机(CPU)、存储器、文件、设备管理打开QQ--相关数据放入内存--进程被CPU处理--视频通话(摄像头设备)向上层提供方便易用的服务GUI(图形用户接口)比如window下拖拽......
  • 数据结构学习中测试代码
    线性表顺序表的一些基本性质//#defineprint(x) std::cout<<x<<std::endl//#defineget(x) std::cin>>x#include<iostream>#include<fstream>usingnamespacestd;#defineInitsize100#typedefstruct{ int*data; intMaxsize,leng......
  • 学习笔记438—《赤兔之死》高考满分文章
    建安二十六年,公元221年,关羽走麦城,兵败遭擒,拒降,为孙权所害。其坐骑赤兔马为孙权赐予马忠。一日,马忠上表:赤兔马绝食数日,不久将亡。孙权大惊,急访江东名士伯喜。此人乃伯乐之后,人言其精通马语。马忠引伯喜回府,至槽间,但见赤兔马伏于地,哀嘶不止。众人不解,惟伯喜知之。伯喜遣散诸人,抚其......
  • C语言学习第三天
    一、选择语句#include<stdio.h>intmain(){intinput=0;printf("学习C语言\n");printf("你要好好学习C语言,然后好好学习数据结构吗?(1/0)/n,1--好好学习,2--不好好学习");scanf("%d",&input);//if(input==0)printf("娶个丑老婆");......
  • springboot+vue--注册
    ***在UserController中声明一个(/register),接口中包括两个功能://用户名是否已被占用//注册**​publicResultregister(Stringusername,Stringpassword){}***在UserService(接口)中,实现两个方法:**​publicUserfindByUsername(Stringusername){}//根据用户......
  • JAVA 学习心得1.22
    JAVA学习1:一、一些小知识1.计算机由软件硬件组成软件—平时用的app等。硬件—鼠标键盘等。2.Java之父——詹姆斯·高斯林,由SUN公司研发。3.使用需要JDK工具包,调整Java环境,PATH等。4.Java具有跨平台性,简单来说就是很多平台都能够运行和编译java语言的文件。二、一切的......
  • 数据库学习笔记(四)—— MySQL 之 事务篇
    MySQL之事务篇事务事务是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。事务的四大特性(ACID):A原子性:原子性是指包含事务的操作要么全部执行......
  • 数据库学习笔记(三)—— MySQL 之 SELECT(查询)篇
    查询单表查询select分组函数,分组后的字段from表名[where条件][groupby分组的字段][having分组后的筛选][orderby排序列表];排序SELECT字段名FROM表名ORDERBY字段名[ASC|DESC];ASC表示升序,DESC表示降序,而ORDERBY默认值为ASC。多字段排......
  • openGauss学习笔记-204 openGauss 数据库运维-常见故障定位案例-重建索引失败
    openGauss学习笔记-204openGauss数据库运维-常见故障定位案例-重建索引失败204.1重建索引失败204.1.1问题现象当Desc表的索引出现损坏时,无法进行一系列操作,可能的报错信息如下。index\"%s\"containscorruptedpageatblock%u",RelationGetRelationName(rel),BufferG......