全局统一返回+全局异常处理

2023/12/21

项目地址:全局统一返回+全局异常处理-gitee-simple分支 (opens new window)

# 全局统一返回+全局异常处理

package com.example.commonresult.result;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 定义全局性的控制器
 * basePackages 作用范围
 * @author lichengcan
 */
@RestControllerAdvice(basePackages = "com.dataojo.haikangbasic")
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    private static final Logger LOG = LoggerFactory.getLogger(ResponseAdvice.class);

    /**
     * 环境变量
     */
    @Value("${spring.profiles.active}")
    private String env;

    /**
     * 是否需要进行统一返回封装
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //定义到类级别:如果类上有这个注解,那就封装
        if (returnType.getDeclaringClass().getAnnotation(ResponseResult.class)!=null) {
            return true;
        }
        //也可以定义到方法级
//        if (returnType.getMethodAnnotation(ResponseResult.class)!=null) {
//            return true;
//        }
        return false;
    }


    /**
     * 此方法才是真正的统一返回封装逻辑
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        // 提供一定的灵活度,如果body已经被包装了,就不进行包装
        if (body instanceof Result || body instansof String) {
            return body;
        }
        //包装类型
        return Result.success(body);
    }


    /**
     * 处理参数校验失败异常
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        String defaultMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
        return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), defaultMessage);
    }

    /**
     * 处理 Request Valid 异常
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    public Result handleBindException(BindException ex) {
        String defaultMessage = ex.getAllErrors().get(0).getDefaultMessage();
        return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), defaultMessage);
    }

    /**
     * 处理 BeanInstantiationException 异常
     */
    @ResponseBody
    @ExceptionHandler(BeanInstantiationException.class)
    public Result handleBeanInstantiationException(BeanInstantiationException ex) {
        Throwable cause = ex.getCause();
        return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), cause.getMessage());
    }

    /**
     * 处理所有其他异常
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception ex) {
        LOG.error("系统异常", ex);
        String message;
        if (ex instanceof HttpMessageNotReadableException) {
            message = "请求参数格式错误";
        } else if (ex instanceof HttpRequestMethodNotSupportedException) {
            message = "不支持的请求方式";
        } else {
            message = "系统繁忙,请稍后再试";
        }
        return buildErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), message);
    }

    /**
     * 构建错误响应
     */
    private Result buildErrorResponse(String code, String message) {
        if ("dev".equals(env)) {
            return Result.failed(code, message);
        } else {
            return Result.failed(code, null);
        }
    }
}

# 自定义注解

package com.example.commonresult.result;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 统一返回注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseResult {
}

# 统一返回数据结构

package com.example.commonresult.result;

import lombok.Data;

/**
 * 统一返回数据结构
 *
 * @param <T>
 */
@Data
public class Result<T> {
    private String code;
    private String message;
    private T data;

    public Result() {
    }

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

    public Result(String code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> Result<T> success(T data) {
        return new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);
    }

    public static Result<?> failed(String code, String message) {
        return new Result<>(code, message);
    }
}

# 枚举

package com.example.commonresult.result;

/**
 * 常用结果的枚举
 */
public enum ResultEnum {
    SUCCESS("00000", "请求成功"),
    VALIDATE_FAILED("2002", "参数校验失败"),
    COMMON_FAILED("2003", "接口调用失败"),
    FORBIDDEN("2004", "没有权限访问资源"),
    USER_EXCEPTION("A0001", "用户端错误"),
    SYSTEM_EXCEPTION("B0001", "系统执行出错"),
    THIRD_PARTY_SERVICE_INVOKE_EXCEPTION("C0001", "调用第三方服务出错");

    private String code;
    private String message;

    // 构造方法
    ResultEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

# 依赖

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

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- alibaba fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

        <!-- spring data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>

# 测试

package com.example.commonresult.controller;

import com.example.commonresult.result.ResponseResult;
import com.example.commonresult.result.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: lichengcan
 * @date: 2023-09-07 15:23
 * @description
 **/
@RestController
@RequestMapping("/test")
@ResponseResult
public class TestController {

    @GetMapping("/test")
    public Object test(Integer num){
        Map map  = new HashMap();
        int j = 10/num;
        map.put(1,1);
        return Result.success(map);
    }

    @GetMapping("/list")
    public List<Integer> list(Integer num){
        List<Integer> list = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            list.add(i);
        }
        return list;
    }

    @GetMapping("/exception")
    public void exception(Integer num){
        int j = 10/num;
    }

    @GetMapping("/testString")
    public String testString(Integer num){
        return "李承灿大帅逼";
    }


    @GetMapping("/testInteger")
    public Integer testInteger(Integer num){
        return num;
    }
}