从零搭建 Spring Boot 后端项目(八)

简介

这一小节主要是做,全局统一异常处理

步骤

Web 应用中经常使用 try-catch 这会导致代码结构杂乱无章的感觉,也不利于调试代码,如果能将所有异常统一到一个模块将是一个很美好的事,那么利用@ControllerAdvice和@ExceptionHandler定义一个统一异常处理类,就能做到全局的异常捕获和处理

  • @ControllerAdvice:控制器增强,使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法
  • @ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法
  • com.example.backend_template.exception 新建GlobalExceptionHandler类
package com.example.backend_template.exception;

import com.example.backend_template.utils.ResultData;
import com.example.backend_template.utils.ResultUtils;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName GlobalExceptionHandler 全局统一处理异常
 * @Description
 * @Author L
 * @Date Create by 2020/7/7
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 该请求控制器存在,但请求HTTP方法与该控制器提供不符
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(405, "Http Request Method Not Supported!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.METHOD_NOT_ALLOWED);
    }

    /**
     * content-type 内容设置类型不支持
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(415, "Http Media Type Not Supported!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.UNSUPPORTED_MEDIA_TYPE);
    }

    /**
     * content-type 内容设置类型不能接受
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(406, "Http Media Type Not Acceptable!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.NOT_ACCEPTABLE);
    }

    /**
     * 缺少路径参数
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MissingPathVariableException.class)
    protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(500, "Missing Path Variable!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 缺少请求参数
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Missing Servlet Request Parameter!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * Servlet请求绑定出错
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(ServletRequestBindingException.class)
    protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Servlet Request Binding!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * 转换不支持
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(ConversionNotSupportedException.class)
    protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(500, "Conversion Not Supported!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 参数类型匹配失败
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(TypeMismatchException.class)
    protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Type Mismatch!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * HTTP 信息请求不可读
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Http Message Not Readable!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * HTTP 信息请求不可写
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(HttpMessageNotWritableException.class)
    protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(500, "Http Message Not Writable!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * 参数校验出错
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Method Argument Not Valid!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * 丢失了一部分请求信息
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MissingServletRequestPartException.class)
    protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Missing Servlet Request Part!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * 参数绑定出错
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(BindException.class)
    protected ResponseEntity<Object> handleBindException(BindException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(400, "Bind Exception!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
    }

    /**
     * 根据请求url找不到控制器,即404异常
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(404, "No Handler Found!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.NOT_FOUND);
    }

    /**
     * 异步请求超时
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(AsyncRequestTimeoutException.class)
    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(503, "Async Request Timeout!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.SERVICE_UNAVAILABLE);
    }

    /**
     * 其它未统一的异常,都由以下handle处理,生产可写网络异常
     *
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(Exception.class)
    protected ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        ResultData<Object> errorBody = ResultUtils.fail(500, "Server Error!", ex.getLocalizedMessage());
        return new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

上面的15个异常处理,包含了SpringMVC在处理请求时可能抛出的异常,其实都是ResponseEntityExceptionHandler里的,你可以继承它,直接使用,我的想法是,都要重写父类的各方法了(为了数据返回类型统一),那干脆不继承了,直接写,这里除了这15个异常以外,还有一个handleAllExceptions是处理其它没有统一的异常,这时,除了这些常见异常,我们还有很多业务异常,这点简单写个示例

  • com.example.backend_template.exception 新建UserNotFoundException类

    package com.example.backend_template.exception;
    
    /**
     * @ClassName UserNotFoundException
     * @Description 
     * @Author L
     * @Date Create by 2020/7/7
     */
    public class UserNotFoundException extends RuntimeException {
    
        public UserNotFoundException() {
            super();
        }
    
        public UserNotFoundException(final String message) {
            super(message);
        }
    
        public UserNotFoundException(final Throwable cause) {
            super(cause);
        }
    
        public UserNotFoundException(final String message, final Throwable cause) {
            super(message, cause);
        }
    }
    
  • 在GlobalExceptionHandler类中新加以下方法

        //以下是自定义异常
        /**
         * 没有找到该用户
         *
         * @param ex
         * @param request
         * @return
         */
        @ExceptionHandler(UserNotFoundException.class)
        protected ResponseEntity<Object> handleUserNotFoundException(UserNotFoundException ex, WebRequest request) {
            ResultData<Object> errorBody = ResultUtils.fail(404, "User Not Found",ex.getLocalizedMessage());
            return new ResponseEntity<>(errorBody, HttpStatus.NOT_FOUND);
        }
    
    
测试
  • 把SecurityController类的root方法,改成如下代码,启动项目并访问http://localhost:8080/
        @RequestMapping("/")
        public String root() {
            throw new AsyncRequestTimeoutException();
    //        return "redirect:/index";
        }
    
  • 出现以下内容,说明我们全局异常统一完成,返回的数据格式也统一了,测试完后,记得改回SecurityController类
    在这里插入图片描述
    当然我们也可以尝试抛出其它的异常,以测试我们是否能返回统一结果,比如自定义的UserNotFoundException异常
    在这里插入图片描述
    其它异常,这里其它异常会统一回复以下结果,这时需要说明的是,生产环境下,动不动抛出异常,会出大问题,更别说500了,这里如果你实在一时解决不了,我们可以报网络异常,这种操作也不知道是那位大神想出来的
    在这里插入图片描述
项目地址

项目介绍:从零搭建 Spring Boot 后端项目
代码地址:https://github.com/xiaoxiamo/backend-template

下一篇

九、单元测试

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小夏陌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值