【SpringBoot笔记18】SpringBoot实现统一异常处理、统一结果响应、统一参数校验

这篇文章,主要介绍如何利用SpringBoot框架实现统一异常处理、统一结果响应、统一参数校验。

目录

一、SpringBoot统一结果响应

1.1、创建工程

1.2、配置FastJson

1.3、创建ResultEnum枚举

1.4、创建Result实体类

二、SpringBoot统一异常处理

2.1、创建自定义异常类

2.2、创建全局异常处理类

三、SpringBoot统一参数校验

3.1、引入参数校验依赖

3.2、创建测试实体类

3.3、创建测试控制器

3.4、运行测试


在实际的项目开发过程中,当应用程序发生异常时,用户可以接收到友好的错误提示信息,而不是直接出现一大堆看不懂的错误信息,这就需要对应用程序的所有异常进行统一的处理,SpringBoot框架提供了统一异常处理的注解,通过相应的注解就可以捕获所有可能出现的异常信息。

此外,在前后端分离的模式下,数据的交互都是采用JSON格式来传递的,如果每一个方法都返回不同格式的数据,这显然不太合适,所以,实际开发中都会将响应结果统一处理,并且对前后端传递的参数进行统一的校验。下面具体介绍如何实现三个统一。

一、SpringBoot统一结果响应

统一结果响应,这里是采用的JSON格式响应所有的数据,所以需要使用FastJson依赖。

1.1、创建工程

首先,创建一个SpringBoot工程,工程中需要引入下面几个依赖。



    org.springframework.boot
    spring-boot-starter-parent
    2.3.0.RELEASE



    
    
        org.springframework.boot
        spring-boot-starter-web
        
        
            
                org.springframework.boot
                spring-boot-starter-json
            
        
    
    
    
        com.alibaba
        fastjson
        1.2.77
    



    
        
            org.springframework.boot
            spring-boot-maven-plugin
            
                
                    
                        repackage
                    
                
            
        
    

1.2、配置FastJson

在工程里面,创建一个【CustomFastJsonConfig】配置类,添加如下配置。

package com.spring.boot.demo.common.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:07
 * @Description FastJson 配置类
 */
@Configuration
public class CustomFastJsonConfig {

    @Bean
    public HttpMessageConverters fastjsonHttpMessageConverters() {
        // 创建 FastJsonHttpMessageConverter 消息转换器对象
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();

        // 创建 FastJsonConfig 配置类对象
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        // 设置编码字符集
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        // 设置日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

        // 设置序列化特征: SerializerFeature 是一个枚举,可以选择不同的序列化特征
        SerializerFeature[] serializerFeatures = new SerializerFeature[]{
                // WriteNullStringAsEmpty: 如果字符串等于 null,那么会被序列化成空字符串 ""
                SerializerFeature.WriteNullStringAsEmpty,
                // WriteNullNumberAsZero: 如果数字等于 null,那么会被序列化成 0
                SerializerFeature.WriteNullNumberAsZero,
                // WriteNullBooleanAsFalse: 如果布尔类型等于 null,那么会被序列化成 false
                SerializerFeature.WriteNullBooleanAsFalse,
                // PrettyFormat: 美化JSON
                SerializerFeature.PrettyFormat
        };
        fastJsonConfig.setSerializerFeatures(serializerFeatures);

        // 配置添加到消息转换器里面
        fastJsonHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

        // 设置响应JSON格式数据
        List mediaTypeList = new ArrayList<>();
        mediaTypeList.add(MediaType.APPLICATION_JSON); // JSON 格式数据
        // 设置消息转换器支持的格式
        fastJsonHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);

        // 返回消息转换器
        return new HttpMessageConverters(fastJsonHttpMessageConverter);
    }
}

1.3、创建ResultEnum枚举

在一个工程里面,可能会出现很多不同的响应结果状态,所以这里采用一个【ResultEnum】枚举类来保存。

  • 这里可以根据自己的需要,继续添加不同的枚举类型。
package com.spring.boot.demo.common.resp;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:13
 * @Description 统一响应结果枚举类
 */
public enum ResultEnum {
    SUCCESS(20000, "响应成功"),
    FAILED(50000, "操作异常");
    
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;

    ResultEnum(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public String getMessage() {
        return message;
    }

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

1.4、创建Result实体类

为了实现统一响应结果,这里创建一个【Result】类,该类作为所有控制器方法的返回值类型。

package com.spring.boot.demo.common.resp;

import java.io.Serializable;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:16
 * @Description 统一响应结果类
 */
public class Result implements Serializable {
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;
    /** 响应数据 */
    private T data;

    /** 私有构造方法 */
    private Result() {}

    /************************** common method **************************/
    /* 响应成功 */
    public static  Result success() {
        Result result = new Result<>();
        result.setStatusCode(ResultEnum.SUCCESS.getStatusCode());
        result.setMessage(ResultEnum.SUCCESS.getMessage());
        return result;
    }

    /* 响应失败 */
    public static  Result failure() {
        Result result = new Result<>();
        result.setStatusCode(ResultEnum.FAILED.getStatusCode());
        result.setMessage(ResultEnum.FAILED.getMessage());
        return result;
    }

    /* 响应失败 */
    public static  Result failure(String message) {
        Result result = new Result<>();
        result.setStatusCode(ResultEnum.FAILED.getStatusCode());
        result.setMessage(message);
        return result;
    }

    /* 响应失败 */
    public static  Result failure(int statusCode, String message) {
        Result result = new Result<>();
        result.setStatusCode(statusCode);
        result.setMessage(message);
        return result;
    }

    /* 响应失败 */
    public static  Result failure(ResultEnum resultEnum) {
        Result result = new Result<>();
        result.setStatusCode(resultEnum.getStatusCode());
        result.setMessage(resultEnum.getMessage());
        return result;
    }

    /* 自定义响应状态码 */
    public Result statusCode(int statusCode) {
        this.setStatusCode(statusCode);
        return this;
    }

    /* 自定义响应提示信息 */
    public Result message(String message) {
        this.setMessage(message);
        return this;
    }

    /* 自定义响应数据 */
    public Result data(T data) {
        this.setData(data);
        return this;
    }

    /************************** getter and setter **************************/
    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    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;
    }

    @Override
    public String toString() {
        return "Result{" +
                "statusCode=" + statusCode +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

到这里,我们就已经把统一响应结果的类定义好啦,下面继续定义统一全局异常处理的类。

二、SpringBoot统一异常处理

2.1、创建自定义异常类

在工程里面,创建一个自定义的异常类【BizException】,这个类主要用于我们在编写业务代码的时候,自定义设置错误的状态码和提示信息。

package com.spring.boot.demo.common.exception;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:30
 * @Description 自定义异常类
 */
public class BizException extends RuntimeException {
    /** 状态码 */
    private Integer statusCode;
    /** 提示信息 */
    private String message;

    public BizException(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    public BizException() {
        super();
    }

    public BizException(String message) {
        this.message = message;
    }

    public BizException(String message, Throwable cause) {
        super(message, cause);
    }

    public BizException(Throwable cause) {
        super(cause);
    }

    protected BizException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public Integer getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    @Override
    public String getMessage() {
        return message;
    }

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

2.2、创建全局异常处理类

在工程里面,创建一个【GlobalExceptionHandler】类,这个类就是处理整个项目里面出现的所有异常的。

  • 这个类上面,需要使用【@ControllerAdvice】注解,表示该类会通过AOP的方式拦截所有Controller层抛出的所有异常。
  • 在该类里面,定义处理异常的方法,并且使用【@ExceptionHandler】注解,指定当前方法处理的异常类型。
package com.spring.boot.demo.common.exception;

import com.spring.boot.demo.common.resp.Result;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:00
 * @Description 全局异常处理类
 */
@ControllerAdvice // 指定AOP拦截Controller层方法
public class GlobalExceptionHandler {

    /**
     * 统一异常处理
     */
    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public Result commonHandler(Exception e) {
        e.printStackTrace();
        // 返回错误信息
        return Result.failure(e.getMessage());
    }

    /**
     * 捕获我们自定义的异常
     */
    @ExceptionHandler(value = {BizException.class})
    @ResponseBody
    public Result bizExceptionHandler(BizException e) {
        e.printStackTrace();
        // 返回错误信息
        return Result.failure(e.getMessage());
    }

    /** 参数校验异常处理 */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result exception(MethodArgumentNotValidException e) {
        e.printStackTrace();
        // 保存错误提示信息
        String message = "参数校验失败";
        // 从异常对象中拿到 ObjectError 对象
        List allErrors = e.getBindingResult().getAllErrors();
        if (!CollectionUtils.isEmpty(allErrors)) {
            // 返回第一个校验失败的参数名称
            message = allErrors.get(0).getDefaultMessage();
        }
        // 返回参数校验信息
        return Result.failure(message);
    }

}

到这里,全局异常处理类就创建好啦,下面继续创建统一参数校验(注意:上面处理参数校验异常的时候,会报错,那是因为没有引入下面两个参数校验的依赖)。

三、SpringBoot统一参数校验

3.1、引入参数校验依赖

参数校验需要引入validation依赖,如下所示:



    javax.validation
    validation-api



    org.hibernate.validator
    hibernate-validator

3.2、创建测试实体类

package com.spring.boot.demo.pojo;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:45
 * @Description
 */
public class User implements Serializable {

    @NotBlank(message = "用户名称不能为空")
    @Size(min = 1, max = 50, message = "用户名称长度必须在1到50之间")
    private String username;

    @NotBlank(message = "用户密码不能为空")
    @Size(min = 1, max = 30, message = "用户密码长度必须在1到30之间")
    private String password;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

3.3、创建测试控制器

package com.spring.boot.demo.controller;

import com.spring.boot.demo.common.exception.BizException;
import com.spring.boot.demo.common.resp.Result;
import com.spring.boot.demo.pojo.User;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/10/29 21:44
 * @Description
 */
@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/success")
    public Result> success() {
        List list = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            User user = new User();
            user.setUsername("text-name00" + i);
            user.setPassword("pass-00" + i);
            user.setEmail("[email protected]");
            list.add(user);
        }
        return Result.>success().data(list);
    }

    @GetMapping("/error")
    public Result failure() {
        int i = 10;
        // 模拟异常情况
        int ans = i / 0;
        return Result.success();
    }

    @GetMapping("/error02")
    public Result failure02() {
        int i = 10;
        // 模拟异常情况
        try {
            int ans = i / 0;
        } catch (Exception e) {
            throw new BizException("抛出自定义异常");
        }
        return Result.success();
    }

    @PostMapping("/error03")
    public Result param(@RequestBody @Valid User user) {
        // 测试参数校验异常, 必须使用【@Valid】注解生效
        return Result.success().data(user);
    }

}

3.4、运行测试

启动工程,使用Postman一次访问上面四个请求地址,可以看到返回结果。

【SpringBoot笔记18】SpringBoot实现统一异常处理、统一结果响应、统一参数校验_第1张图片

到此,SpringBoot统一异常处理、统一结果响应、统一参数校验就成功啦。

综上,这篇文章结束了,主要介绍如何利用SpringBoot框架实现统一异常处理、统一结果响应、统一参数校验。

你可能感兴趣的:(SSM专栏,spring,boot,spring,统一异常处理,统一结果响应,统一参数校验)