Spring boot统一错误处理

Spring boot统一错误处理

期望定义一种规范,能让前端非常方便地处理接口错误。那么问题来了,前端怎么处理接口比较方便呢?

前端脚手架通常会为所有的API请求写一个拦截器,其中拦截器很重要的一个能力就是对数据进行预处理。假设所有的正确数据都走向ajaxsuccess句柄,所有异常的数据都走向error句柄,成功或者失败的结果不会互相穿插,我们在定义句柄时无疑可以省却很多if-else分支。

这是其一,其二:所有成功的数据都是ResultEnry格式,所有的错误都是errorcode+errormsg散列。我们在前端拦截器error句柄可以凭借errorcode联动错误信息配置非常方便的进行报错弹窗,省去大量接口类异常的逻辑代码。

所以此处统一错误处理应满足两点:

  1. 后台接口不应该捕获并处理数据异常
  2. 抛给前端的异常统一格式

自定义返回实体

略。参考另一篇《Spring boot自定义返回实体》

统一异常处理的两种方法

  1. 使用注解ControllerAdvice
  2. 实现ErrorController

本例选择ControllerAdvice+自定义异常实现统一规范。下面简单介绍两种方式。

ControllerAdvice

该注解定义一个异常类,类里面定义多种异常句柄。

@ControllerAdvice
public class WebExceptionHandler {
    @ExceptionHandler
    public Result<Object> unknownException(Exception e) {
        return ResultEntry.error(ResultEnum.UNKNOWN_ERROR);
    }
    @ExceptionHandler
    public Result<Object> ioException(IOException e) {
        return ResultEntry.error(ResultEnum.UNKNOWN_ERROR);
    }
}

此方法只能监听到控制器抛出的异常

ErrorController

此方式会让框架默认的BaseErrorController无效

@RestController
public class WebExceptionHandler implements ErrorController {
    public Result<Object> error(HttpServletRequest request, HttpServletResponse response) {
        return ResultEntry.error(ResultEnum.UNKNOWN_ERROR);
    }
}

自定义运行时异常

业务代码捕捉到异常,然后通过自定义异常层层往上抛,结合状态码枚举ResultEnum可以达到统一格式精准提示。

自定义异常ServiceException

public class ServiceException extends RuntimeException{
    private ResultEnum error;

    public ServiceException(ResultEnum error) {
        this.error = error;
    }

    public ResultEnum getError() {
        return error;
    }

    public void setError(ResultEnum error) {
        this.error = error;
    }
}

ControllerAdvice异常句柄

@ControllerAdvice
public class ServiceExceptionHander {
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public <T> ResponseEntity<Result<T>> handle(ServiceException e){
        ResponseEntity<Result<T>> result = ResultEntry.error(e.getError());
        return result;
    }
}

ResultEntry组装返回的数据结构,利用ResponseEntity传递给前端错误码和错误实体。所以如果接口能拿到正确的数据,返回HttpStatus.OK,否则返回HttpStatus.BAD_GATEWAY;前端也会相应的触发ajaxsuccess或者error句柄。

public class ResultEntry<T> implements Serializable{

    public static <T> ResponseEntity<Result<T>> success(T o) {
        return ResultEntry.response(ResultEnum.SUCCESS, o);
    }
    
    public static <T> ResponseEntity<Result<T>> success() {
        return ResultEntry.response(ResultEnum.SUCCESS);
    }

    public static <T> ResponseEntity<Result<T>> response(ResultEnum enu, T o) {
        Result<T> result = new Result<T>();
        result.setMsg(enu.getMsg());
        result.setStatus(enu.getStatus());
        result.setBody(o);
        return new ResponseEntity<Result<T>>(result, HttpStatus.OK);
    }

    public static <T> ResponseEntity<Result<T>> response(ResultEnum enu) {
        Result<T> result = new Result<T>();
        result.setMsg(enu.getMsg());
        result.setStatus(enu.getStatus());
        return new ResponseEntity<Result<T>>(HttpStatus.OK);
    }

    public static <T> ResponseEntity<Result<T>> error(ResultEnum enu) {
        Result<T> result = new Result<T>();
        result.setMsg(enu.getMsg());
        result.setStatus(enu.getStatus());
        return new ResponseEntity<Result<T>>(result, HttpStatus.BAD_GATEWAY);
    }
}

测试

//controller
@RestController()
public class UpgradeController {
    @Autowired
    private UpgradeService upgradeService;

    @RequestMapping(value = "upgrade/records", method = RequestMethod.GET)
    public ResponseEntity<Result<ResultPage<UpgradeRecordDTO>>> getUpgradeRecords() {
        ResultPage<UpgradeRecordDTO> data = upgradeService.getUpgradeRecords();
        return ResultEntry.success(data);
    }
}

//service
@Service
public class UpgradeService {
    @Autowired
    private UpgradeRecordRepository recordRepository;
    // 查升级记录
    public ResultPage<UpgradeRecordDTO> getUpgradeRecords() throws ServiceException {
        Pageable pageable = PageRequest.of(0, 10);
        return new ResultPage<>(recordRepository.findAll(pageable));
    }
}
posted @ 2021-12-28 16:12  乐小天  阅读(483)  评论(0编辑  收藏  举报