SpringBoot 统一结果返回及异常统一处理

该文介绍了如何在SpringBoot应用中封装JSON响应状态(ApiStatus枚举)和统一异常处理。通过ApiResult类定义了标准的返回数据格式,包括状态码、错误信息和数据。同时,定义了ApiException基类以及特定类型的异常如ApiParamException,用于参数异常。最后,通过ResponseAdvice控制器建议类实现全局异常捕获和自定义响应体。
摘要由CSDN通过智能技术生成

统一封装Json和异常处理

ApiStatus

org.springframework.http.HttpStatus 没办法使用 @JsonValue 注解, 所以封装一层


import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

/**
 * @author ZJ.Fang
 * @date 2023/4/13 10:29
 */
@Getter
@AllArgsConstructor
public enum ApiStatus {

    OK(HttpStatus.OK.value(), "请求成功"),
    BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "参数异常"),
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部异常");

    @JsonValue
    private final Integer value;
    private final String desc;

}

ApiResult

统一返回的数据格式


import lombok.Getter;
import lombok.Setter;

/**
 * @author ZJ.Fang
 * @date 2023/4/13 9:56
 */
@Getter
@Setter
public class ApiResult<T> {

    private ApiStatus code;

    private String msg;

    private T data;

    public static <T> ApiResult<T> success(T data) {
        ApiStatus status = ApiStatus.OK;
        return new ApiResult<>(status, status.getDesc(), data);
    }

    public static <T> ApiResult<T> failure(ApiStatus status, String msg) {
        return new ApiResult<>(status, msg);
    }

    public static <T> ApiResult<T> failure(ApiStatus status, String msg, T t) {
        return new ApiResult<>(status, msg, t);
    }

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

    private ApiResult(ApiStatus status, String msg) {
        this.code = status;
        this.msg = msg;
    }

    private ApiResult(ApiStatus status, String msg, T data) {
        this.code = status;
        this.msg = msg;
        this.data = data;
    }
}

ApiException

import lombok.Getter;
import lombok.Setter;

/**
 * API异常基类
 * @author ZJ.Fang
 */
@Getter
@Setter
public class ApiException extends RuntimeException {

    private ApiStatus status;

    public ApiException(ApiStatus status, String message) {
        super(message);
        this.status = status;
    }

    public ApiException(ApiStatus status, String message, Throwable cause) {
        super(message, cause);
        this.status = status;
    }

}

ApiParamException

/**
 * 参数异常
 * @author ZJ.Fang
 */
public class ApiParamException extends ApiException {

    public ApiParamException(String message) {
        super(ApiStatus.BAD_REQUEST, message);
    }

    public ApiParamException(String message, Throwable cause) {
        super(ApiStatus.BAD_REQUEST, message, cause);
    }
}

ResponseAdvice

import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
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.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 org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @author ZJ.Fang
 * @date 2023/4/13 10:00
 */
@Slf4j
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 这个地方如果返回false ,不会执行 beforeBodyWrite 方法
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        Class<?> type = returnType.getParameterType();
        return type != ApiResult.class;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return ApiResult.success(body);
    }

    @ResponseBody
    @SuppressWarnings("unused")
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ApiResult<Object> handleException(HttpServletRequest request, Exception ex) {
        log.error(ex.getMessage(), ex);
        return ApiResult.failure(ApiStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), getStackTrace(ex));
    }

    /**
     * 异常堆栈信息拼接
     * @param exception 异常
     * @return 堆栈信息
     */
    private String getStackTrace(Exception exception) {
        String info = String.format("%s:%s%n", exception.getClass().getName(), exception.getMessage());
        StringBuilder msg = new StringBuilder(info);
        StackTraceElement[] stackTrace = exception.getStackTrace();

        for (StackTraceElement stackTraceElement : stackTrace) {
            StringBuilder fileName = new StringBuilder();
            if (stackTraceElement.getFileName() != null) {
                fileName.append(stackTraceElement.getFileName());
                if (stackTraceElement.getLineNumber() > 0) {
                    fileName.append(":").append(stackTraceElement.getLineNumber());
                }
            } else {
                fileName.append("Unknown Source");
            }
            msg.append(stackTraceElement.getClassName()).append(".").append(stackTraceElement.getMethodName()).append("(").append(fileName).append(")").append(String.format("%n"));
        }
        //返回信息
        return msg.toString();
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ApiException.class)
    public ApiResult<Object> apiExceptionHandle(Exception ex) {
        log.error(ex.getMessage(), ex);
        ApiException apiException = (ApiException) ex;
        return ApiResult.failure(apiException.getStatus(), apiException.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ApiParamException.class)
    public ApiResult<Object> paramExceptionHandle(Exception ex) {
        log.error(ex.getMessage(), ex);
        ApiParamException apiException = (ApiParamException) ex;
        return ApiResult.failure(apiException.getStatus(), appendParamMsg(apiException.getMessage()));
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public ApiResult<Object> bindExceptionHandle(Exception ex) {
        String msg = getMsgByBindException((BindException) ex);
        log.error(msg, ex);
        return ApiResult.failure(ApiStatus.BAD_REQUEST, msg);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResult<Object> constraintViolationExceptionHandle(Exception ex) {
        log.error(ex.getMessage(), ex);
        return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(ex.getMessage()));
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult<Object> methodArgumentNotValidExceptionHandle(Exception ex) {
        log.error(ex.getMessage(), ex);
        MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
        String msg = getMsgByBindResult(exception.getBindingResult());
        return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(msg));
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ApiResult<Object> missingServletRequestParameterExceptionHandle(MissingServletRequestParameterException exception) {
        log.error(exception.getMessage(), exception);
        return ApiResult.failure(ApiStatus.BAD_REQUEST, appendParamMsg(exception.getMessage()));
    }

    private String getMsgByBindException(BindException bindException) {
        BindingResult bindingResult = bindException.getBindingResult();
        return getMsgByBindResult(bindingResult);
    }

    private static String getMsgByBindResult(BindingResult bindingResult) {
        List<ObjectError> errors = bindingResult.getAllErrors();
        return errors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
    }

    private String appendParamMsg(String msg) {
        return ApiStatus.BAD_REQUEST.getDesc()+ "," + msg;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值