SpringBoot全局异常机制+RESTful统一规范


SpringBoot-23-全局异常机制+RESTful统一规范

1.为什么需要全局异常机制?

如果我们团队在开发项目的时候,程序出现异常是正常的,比如因为业务操作没有按照流程,程序的运行异常等等,我们不可能也不应该每一处异常进行单独处理,或者不处理将异常信息直接抛出给用户,这样会导致用户体验差。

因此设置统一的异常处理机制具有以下好处:

那么我们需要如何的开发规范呢?

之前章节也讲过

不熟悉的朋友可以去看一下。

代码实现

RESTful统一返回规范设置

我们在上一章节实际上已经介绍过了,但是怕一些人没有看过,这里再进行介绍一次

/**
 * 统一返回结果接口
 */
public interface IResultCode {
    /**
     * 返回码
     *
     * @return int
     */
    int getCode();
    /**
     * 返回消息
     *
     * @return String
     */
    String getMsg();
}
@Getter
@AllArgsConstructor
public enum  ResultCode implements  IResultCode{
    /**
     * 操作成功
     */
    SUCCESS(200, "操作成功"),
    /**
     * 业务异常
     */
    FAILURE(400, "业务异常"),
    /**
     * 服务异常
     */
    ERROR(500, "服务异常"),
    /**
     * 参数错误
     */
    GLOBAL_PARAM_ERROR(4000, "参数错误");
    /**
     * 状态码
     */
    final int code;
    /**
     * 消息内容
     */
    final String msg;
}
@Data
@Getter
public class Result implements Serializable {
    private static final long serialVersionUID = 1L;
    private int code;
    private String msg;
    private long time;
    private T data;
    private Result() {
        this.time = System.currentTimeMillis();
    }
    private Result(IResultCode resultCode) {
        this(resultCode, null, resultCode.getMsg());
    }
    private Result(IResultCode resultCode, String msg) {
        this(resultCode, null, msg);
    }
    private Result(IResultCode resultCode, T data) {
        this(resultCode, data, resultCode.getMsg());
    }
    private Result(IResultCode resultCode, T data, String msg) {
        this(resultCode.getCode(), data, msg);
    }
    private Result(int code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        this.time = System.currentTimeMillis();
    }
    /**
     * 返回状态码
     *
     * @param resultCode 状态码
     * @param         泛型标识
     * @return ApiResult
     */
    public static  Result success(IResultCode resultCode) {
        return new Result<>(resultCode);
    }
    public static  Result success(String msg) {
        return new Result<>(ResultCode.SUCCESS, msg);
    }
    public static  Result success(IResultCode resultCode, String msg) {
        return new Result<>(resultCode, msg);
    }
    public static  Result data(T data) {
        return data(data, "处理成功");
    }
    public static  Result data(T data, String msg) {
        return data(ResultCode.SUCCESS.code, data, msg);
    }
    public static  Result data(int code, T data, String msg) {
        return new Result<>(code, data, data == null ? "承载数据为空" : msg);
    }
    public static  Result fail() {
        return new Result<>(ResultCode.FAILURE, ResultCode.FAILURE.getMsg());
    }
    public static  Result fail(String msg) {
        return new Result<>(ResultCode.FAILURE, msg);
    }
    public static  Result fail(int code, String msg) {
        return new Result<>(code, null, msg);
    }
    public static  Result fail(IResultCode resultCode) {
        return new Result<>(resultCode);
    }
    public static  Result fail(IResultCode resultCode, String msg) {
        return new Result<>(resultCode, msg);
    }
    public static  Result condition(boolean flag) {
        return flag ? success("处理成功") : fail("处理失败");
    }
}

全局异常处理

@Data
public class BaseException extends RuntimeException {
    private static final long serialVersionUID = 5782968730281544562L;
    private int status = INTERNAL_SERVER_ERROR.value();
    public BaseException() {
    }
    public BaseException(String message) {
        super(message);
    }
    
    public BaseException(int status, String message) {
        super(message);
        this.status = status;
    }
}
@Data
public class ValidateCodeException extends RuntimeException {
    private static final long serialVersionUID = -7285211528095468156L;
    private int status = INTERNAL_SERVER_ERROR.value();
    public ValidateCodeException() {
    }
    public ValidateCodeException(String msg) {
        super(msg);
    }
    public ValidateCodeException(int code, String message) {
        super(message);
        this.status = code;
    }
}

使用**@RestControllerAdvice+@ExceptionHandler**

具体介绍可以看我的全局异常处理介绍这里只说实现过程

@Slf4j
@ResponseBody
@RestControllerAdvice
public class BaseExceptionHandler {
    /**
     * BaseException 异常捕获处理
     * @param ex 自定义BaseException异常类型
     * @return Result
     */
    @ExceptionHandler(BaseException.class)
    public Result<?> handleException(BaseException ex) {
        log.error("程序异常:" + ex.toString());
        return Result.fail(ex.getStatus(), ex.getMessage());
    }
    /**
     * BaseException 异常捕获处理
     * @param ex 自定义BaseException异常类型
     * @return Result
     */
    @ExceptionHandler(ValidateCodeException.class)
    @ResponseStatus
    public Result<?> handleValidateCodeException(ValidateCodeException  ex) {
        log.error("验证码错误:" + ex.getMessage());
        return Result.fail(ex.getStatus(), ex.getMessage());
    }
    
    /**
     * FileNotFoundException,NoHandlerFoundException 异常捕获处理
     * @param exception 自定义FileNotFoundException异常类型
     * @return Result
     */
    @ExceptionHandler({FileNotFoundException.class, NoHandlerFoundException.class})
    public Result<?> noFoundException(Exception exception) {
        log.error("程序异常==>errorCode:{}, exception:{}", HttpStatus.NOT_FOUND.value(), exception.getMessage());
        return Result.fail(HttpStatus.NOT_FOUND.value(), exception.getMessage());
    }
    /**
     * NullPointerException 空指针异常捕获处理
     * @param ex 自定义NullPointerException异常类型
     * @return Result
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleException(NullPointerException ex) {
        log.error("程序异常:{}" + ex.toString());
        return Result.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
    }
    /**
     * 通用Exception异常捕获
     * @param ex 自定义Exception异常类型
     * @return Result
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleException(Exception ex) {
        log.error("程序异常:" + ex.toString());
        String message = ex.getMessage();
        if (StringUtils.contains(message, "Bad credentials")){
            message = "您输入的密码不正确";
        } else if (StringUtils.contains(ex.toString(), "InternalAuthenticationServiceException")) {
            message = "您输入的用户名不存在";
        }
        return Result.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }
}

控制层TestController的实现

@RequestMapping("test")
@RestController
public class TestController {
    @GetMapping("/base/{name}")
    public void BaseException(@PathVariable("name") String name) {
        System.out.println("Hello: BaseException "+ name);
        throw  new BaseException(HttpStatus.MULTI_STATUS,"错误");
    }
    @GetMapping("/valid/{name}")
    public void ValidateCodeException(@PathVariable("name") String name) {
        System.out.println("Hello:  ValidateCodeException "+ name);
        throw  new ValidateCodeException(ResultCode.GLOBAL_PARAM_ERROR.getCode(),ResultCode.GLOBAL_PARAM_ERROR.getMsg());
    }
}

测试

使用postman分别测试

测试结果如下:

SpringBoot-23-全局异常机制+RESTful统一规范

在上面图标记处不知道大家发现一个问题,自定义业务状态码和HttpStatus状态码存在不一致的情况,如果相应自定义的业务状态码,在HttpStatus存在,相应HttpStatus和自定义状态码一致需要怎么办呢?

HTTP和自定义状态码一致代码实现

@Component
@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        //如果响应结果是JSON数据类型
        if(selectedContentType.equalsTypeAndSubtype(
                MediaType.APPLICATION_JSON)){
            int code= ((Result) body).getCode();
            if(code>0 && code<512) {
                //HTTP响应结果设置状态码,状态码就是IResultCode的code,二者达到统一
                response.setStatusCode(
                        HttpStatus.valueOf(((Result) body).getCode())
                );
            }
            return body;
        }
        return body;
    }
}

测试结果:

SpringBoot-23-全局异常机制+RESTful统一规范

如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!

公众号 springboot葵花宝典 主要分享JAVA技术,回复: springboot 获取springboot相关代码视频资料

原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!

展开阅读全文

更新时间:2024-08-19

标签:全局   异常   机制   接口   状态   类型   代码   业务   测试   程序

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top