springboot异常处理

文章介绍了在三层架构中处理异常的常见问题,如返回的JSON数据不符合开发规范。提出了解决方案,包括在每个Controller方法中使用try-catch块(不推荐)和创建全局异常处理器(推荐)。全局异常处理器通过@RestControllerAdvice注解定义,并使用@ExceptionHandler捕获特定类型的异常,返回符合规范的Result对象。此外,文章还讨论了如何扩展异常处理器来处理不同类型的HTTP错误,并结合自定义异常进行更精确的错误响应。
摘要由CSDN通过智能技术生成

1.当没有异常处理的时候

一般如果系统发生异常,接口就状态码就会返回500,返回回来一个json数据。

响应回来的数据是一个JSON格式的数据。但这种JSON格式的数据还是我们开发规范当中所提到的统一响应结果Result吗?显然并不是。由于返回的数据不符合开发规范,所以前端并不能解析出响应的JSON数据。

当我们没有做任何的异常处理时,我们三层架构处理异常的方案:
Mapper接口在操作数据库的时候出错了,此时异常会往上抛(谁调用Mapper就抛给谁),会抛给service。
service 中也存在异常了,会抛给controller。
而在controller当中,我们也没有做任何的异常处理,所以最终异常会再往上抛。最终抛给框架之后,框架就会返回一个JSON格式的数据,里面封装的就是错误的信息,但是框架返回的JSON格式的数据并不符合我们的开发规范。

2.解决方案

那么在三层构架项目中,出现了异常,该如何处理?
方案一:在所有Controller的所有方法中进行try…catch处理
缺点:代码臃肿(不推荐)
方案二:全局异常处理器
好处:简单、优雅(推荐)

3.全局异常处理器

我们该怎么样定义全局异常处理器?
定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。
在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。

@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler(Exception.class) //指定能够处理的异常类型
    public Result ex(Exception e){
        e.printStackTrace();//打印堆栈中的异常信息

        //捕获到异常之后,响应一个标准的Result
        return Result.error(500,"对不起,操作失败,请联系管理员");
    }
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

处理异常的方法返回值会转换为json后再响应给前端。

4.异常处理器拓展

对400和405等客户端错误产生的异常进行补抓,并把返回码统一管理。

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

    /**
     * 请求参数异常/缺少--400
     *
     * @param e
     * @return
     */
    @ExceptionHandler({
            MissingServletRequestParameterException.class,
            MethodArgumentTypeMismatchException.class,
            RequestRejectedException.class,
            BindException.class}
    )
    public Result missingServletRequestParameterException(Exception e) {
        return Result.error(StatusCode.PARAMETERERROR, "缺少参数或参数错误");
    }

    /**
     * 请求方法错误--405
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportedExceptionHandler(Exception e){
        log.error("请求方法错误");
        return Result.error(StatusCode.METHODERROR,"请求方法错误");
    }


    /**
     * 其他异常--500
     */
    @ExceptionHandler(Exception.class)
    public Result exceptionHandler(Exception e) {
        e.printStackTrace();
        return Result.error(StatusCode.SERVERERROR,"对不起,操作失败,请联系管理员");
    }
}

状态码统一管理:

public class StatusCode {
    /**
     * 操作成功
     */
    public static final int OK = 200;

    /**
     * 请求参数异常或缺失
     */
    public static final int PARAMETERERROR = 400;

    /**
     * 资源不存在
     */
    public static final int NOTFOUND = 404;

    /**
     * 请求方法错误
     */
    public static final int METHODERROR = 405;

    /**
     * 服务端错误
     */
    public static final int SERVERERROR = 500;

}

5.异常处理器与自定义异常结合

有一种设计是我们Result.error之类返回错误相应对的都会集中到全局异常处理器就行处理。

先继承至运行异常书写一个通用的异常类,多一个code,全局异常处理器处理时候便于返回响应的错误代码。

@Getter
public class CommonException extends RuntimeException{
    private int code;

    public CommonException(String message, int code) {
        super(message);
        this.code = code;
    }

    public CommonException(String message, Throwable cause, int code) {
        super(message, cause);
        this.code = code;
    }

    public CommonException(Throwable cause, int code) {
        super(cause);
        this.code = code;
    }
}

然后我们就可以让其他类继承这个异常类生成新的异常类。

例:

public class DbException extends CommonException{

    public DbException(String message) {
        super(message, 500);
    }

    public DbException(String message, Throwable cause) {
        super(message, cause, 500);
    }

    public DbException(Throwable cause) {
        super(cause, 500);
    }
}

然后在需要抛出异常的地方抛出异常,然后在全局异常处理器中对我们定义的不同异常进行补抓处理:

@RestControllerAdvice
@Slf4j
public class CommonExceptionAdvice {

    @ExceptionHandler(DbException.class)
    public Object handleDbException(DbException e) {
        log.error("mysql数据库操作异常 -> ", e);
        return processResponse(e);
    }

    @ExceptionHandler(CommonException.class)
    public Object handleBadRequestException(CommonException e) {
        log.error("自定义异常 -> {} , 异常原因:{}  ",e.getClass().getName(), e.getMessage());
        log.debug("", e);
        return processResponse(e);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getAllErrors()
                .stream().map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining("|"));
        log.error("请求参数校验异常 -> {}", msg);
        log.debug("", e);
        return processResponse(new BadRequestException(msg));
    }
    @ExceptionHandler(BindException.class)
    public Object handleBindException(BindException e) {
        log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
        log.debug("", e);
        return processResponse(new BadRequestException("请求参数格式错误"));
    }

    @ExceptionHandler(NestedServletException.class)
    public Object handleNestedServletException(NestedServletException e) {
        log.error("参数异常 -> NestedServletException,{}", e.getMessage());
        log.debug("", e);
        return processResponse(new BadRequestException("请求参数处理异常"));
    }

    @ExceptionHandler(Exception.class)
    public Object handleRuntimeException(Exception e) {
        log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
        return processResponse(new CommonException("服务器内部异常", 500));
    }

    private ResponseEntity<R<Void>> processResponse(CommonException e){
        return ResponseEntity.status(e.getCode()).body(R.error(e));
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot提供了一套默认的异常处理机制。一旦程序出现异常,SpringBoot会向/error的URL发送请求,并通过BasicErrorController来处理该请求。默认情况下,SpringBoot会跳转到默认显示异常信息的页面来展示异常信息。如果我们希望将所有的异常统一跳转到自定义的错误页面,可以在src/main/resources/templates目录下创建一个名为error.html的页面。通过覆盖默认的错误页面,我们可以实现自定义的异常处理。 除了使用SpringBoot的默认配置外,还可以通过自定义错误页面来处理异常。我们可以在src/main/resources/templates目录下创建error.html页面,并将其命名为error。通过这种方式,我们可以自定义错误页面的内容和样式来展示异常信息。 在处理异常的过程中,可以关注ErrorMvcAutoConfiguration中的三个关键点。通过对SpringBoot错误处理机制源码的跟踪,我们可以更深入地了解异常处理的实现细节。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot异常处理](https://blog.csdn.net/Linging_24/article/details/126077782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot 异常处理详解](https://blog.csdn.net/qq_42402854/article/details/91415966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值