返回值封装,异常统一处理优雅解决接口所有问题

在项目整体架构设计的时候,我们经常需要做以下工作:

  1. 返回值的统一封装处理,因为只有规范好统一的返回值格式,才能不会给接口使用者带来疑惑和方便前端对接口的统一处理。
  2. 对异常码进行严格规定,什么错误返回什么码制,这样前端就可以根据不同的码制进行错误提示,同时也方便三方调用者对异常进行处理。要注意,微服务架构,不同的微服务之间的码制前缀要做区分,这样我们就可以根据码制很快定位到是哪个微服务出现问题。
  3. 对异常的统一拦截,这一点非常重要,因为一旦对异常没有做统一处理,严重就会导致堆栈乃至sql信息暴露给前端,这是非常严重的事故。

所以今天我会给大家展示如何对返回值进行统一封装,如何对异常进行统一拦截,异常码定义,根据自己的业务自行定义即可。

1. 返回值的封装

分为两步,首先定义返回值类型和字段,然后定义BaseController。

1.1 响应信息主体

我定义的这个涵盖了我们要返回的各种情况,大家可以参考

/**
 * 响应信息主体
 */
@Data
@NoArgsConstructor
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 成功
     */
    public static final int SUCCESS = 200;

    /**
     * 失败
     */
    public static final int FAIL = 500;

    private int code;

    private String msg;

    private T data;

    public static <T> R<T> ok() {
        return restResult(null, SUCCESS, "操作成功");
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, SUCCESS, "操作成功");
    }

    public static <T> R<T> ok(String msg) {
        return restResult(null, SUCCESS, msg);
    }

    public static <T> R<T> ok(String msg, T data) {
        return restResult(data, SUCCESS, msg);
    }

    public static <T> R<T> fail() {
        return restResult(null, FAIL, "操作失败");
    }

    public static <T> R<T> fail(String msg) {
        return restResult(null, FAIL, msg);
    }

    public static <T> R<T> fail(T data) {
        return restResult(data, FAIL, "操作失败");
    }

    public static <T> R<T> fail(String msg, T data) {
        return restResult(data, FAIL, msg);
    }

    public static <T> R<T> fail(int code, String msg) {
        return restResult(null, code, msg);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static <T> R<T> warn(String msg) {
        return restResult(null, HttpStatus.WARN, msg);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static <T> R<T> warn(String msg, T data) {
        return restResult(data, HttpStatus.WARN, msg);
    }

    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }

    public static <T> Boolean isError(R<T> ret) {
        return !isSuccess(ret);
    }

    public static <T> Boolean isSuccess(R<T> ret) {
        return R.SUCCESS == ret.getCode();
    }
}

1.2 BaseController 的写法

**
 * web层通用数据处理
 */
public class BaseController {

    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected R<Void> toAjax(int rows) {
        return rows > 0 ? R.ok() : R.fail();
    }

    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected R<Void> toAjax(boolean result) {
        return result ? R.ok() : R.fail();
    }

	//同时也可以将一些常用的方法放在这里,比如获取登录用户信息
}

1.3 示例

@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/category")
public class WfCategoryController extends BaseController {

 @GetMapping("/eventTest")
    public R<Void> eventTest(){

        applicationEventPublisher.publishEvent(new UserChangePasswordEvent("11111"));

        return R.ok();
    }
}

返回值:
在这里插入图片描述

2. 全局异常拦截

全局异常拦截的本质实际上采用的是spring的AOP拦截实现的,然后根据异常类型映射到不同的ExceptionHandler处理方法上。

2.1 引入AOP依赖

必须引入AOP依赖,我刚开始忘了依赖,怎么试都不对,所以一定要检查好,不要忘了。

        <!-- SpringBoot 拦截器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.2 写异常拦截器

在写异常拦截器的时候,我们得要分为这么几个部分,首先是自定义的业务异常拦截,验证类异常,比如参数校验类异常拦截处理,还有未知的运行时异常拦截,如果使用到Mybatis,还得要注意Mybatis系统异常的通用拦截处理,还有就是请求不支持异常,最后再是统一的大异常拦截。

可以看我下面写的案例:

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                       HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.fail(e.getMessage());
    }

    /**
     * Mybatis系统异常 通用处理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String message = e.getMessage();
        if (message.contains("CannotFindDataSourceException")) {
            log.error("请求地址'{}', 未找到数据源", requestURI);
            return R.fail("未找到数据源,请联系管理员确认");
        }
        log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
        return R.fail(message);
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return R.fail(e.getMessage());
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public R<Void> handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
        return R.fail(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R<Void> constraintViolationException(ConstraintViolationException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
        return R.fail(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.fail(message);
    }
}

运行返回显示
在这里插入图片描述

异常统一处理和返回值封装就到此结束了,上面的稍微修改下,完全可以做生产用,希望大家多多点赞关注支持,后续给大家分享更多有用的知识点!!!

返回值处理问题通常发生在函数或方法执行完毕后,预期有个明确的结果返回,但实际上没有正确地获取或处理返回值。要解决这个问题,你可以按照以下步骤进行: 1. **明确返回值**:确保函数或方法有明确的返回类型,并且当执行完任务后返回期望的数据结构。例如,如果你的函数应该返回一个列表,就不要让它返回`null`或者`void`。 2. **接收返回值**:在调用函数的地方,给它一个变量来存储返回值。如果函数没有返回值,记得检查文档或代码注释以了解它的行为。 ```java List<String> result = myFunctionThatReturnsList(); if (result != null) { // 处理结果 } ``` 3. **错误处理**:对于可能抛出异常的操作,捕获异常并适当地处理,防止因为异常导致程序中断而无法获取返回值。 ```java try { int value = myMethodThatCouldThrow(); // 使用value } catch (Exception e) { handleException(e); } ``` 4. **测试覆盖**:编写单元测试来验证函数的返回结果,特别是在更改了函数的行为之后。 5. **查看日志**:如果返回值在实际应用中没有生效,查看日志可以帮助找出问题所在,例如函数内部的日志信息或者系统日志。 6. **代码审查**:有时,代码逻辑复杂,可能需要同事的帮助进行审查,看看是否存在理解偏差或遗漏的部分。 记住,良好的编程习惯包括对函数行为的明确预期和有效反馈,这将有助于避免此类问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术闲聊DD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值