Spring Boot全局异常捕获处理

本节要实现的是,当我们请求的Controller接口有错误时,可以全局捕获到相应的异常,并返回给用户,这里使用的是通过ControllerAdvice和ExceptionHandler捕获异常和错误信息,向前端返回json格式的状态码及异常描述信息。

1.创建一个全局异常捕获处理类GlobalExceptionHandler


import com.qiqi.utils.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
 * 全局异常捕获处理
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /**
     * http请求的方法不正确
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public String httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
        logger.error("http请求的方法不正确:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.RequestMethodNotAllowed);
    }
 
    /**
     * 请求参数不全
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public String missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        logger.error("请求参数不全:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.MissingServletRequestParameter);
    }
 
    /**
     * 请求参数类型不正确
     */
    @ExceptionHandler(TypeMismatchException.class)
    @ResponseBody
    public String typeMismatchExceptionHandler(TypeMismatchException e) {
        logger.error("请求参数类型不正确:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.TypeMismatchException);
    }
 
    /**
     * 数据格式不正确
     */
    @ExceptionHandler(DataFormatException.class)
    @ResponseBody
    public String dataFormatExceptionHandler(DataFormatException e) {
        logger.error("数据格式不正确:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.DataFormatException);
    }
 
    /**
     * 用户没找到
     */
    @ExceptionHandler(UserNotFoundException.class)
    @ResponseBody
    public String userNotFoundExceptionHandler(UserNotFoundException e) {
        logger.error("用户没找到:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.UserNotExist);
    }
 
    /**
     * 非法输入
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public String illegalArgumentExceptionHandler(IllegalArgumentException e) {
        logger.error("非法输入:【"+e.getMessage()+"】");
        return ExceptionUtil.resultOf(ResultStatusCode.IllegalArgumentException);
    }
 
 
    @ExceptionHandler  //处理其他异常
    @ResponseBody
    public String allExceptionHandler(Exception e){
        logger.error("具体错误信息:【"+ExceptionUtil.getErrorMessage(e)+"】"); //会记录出错的代码行等具体信息
        int count = 0; //只打印15行的错误堆栈
        for (StackTraceElement stackTraceElement : e.getStackTrace()) {
            logger.error(stackTraceElement.toString());
            if(count++ > 13) break;
        }
        return ExceptionUtil.resultOf(ResultStatusCode.SystemException);
    }
 
}

2.自定义了一些异常UserNotFoundException、DataFormatException等。

public class UserNotFoundException extends Exception {
    public UserNotFoundException() {
    }
 
    public UserNotFoundException(String message) {
        super(message);
    }
 
    public UserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}
 

3.自定义一个异常状态码的枚举类 ResultStatusCode


public enum ResultStatusCode {
 
    Success("0", "Success"),
    UserNotExist("1", "User not exist"),
    InValidParameter("2","Invalid parameter"),
    DataFormatException("4", "DataFormat exception"),
    DataNotExistException("5", "DataNotExistException"),
    TimeFormatException("6","TimeFormat Exception"),
    PictureFormatException("7","PictureFormat Exception"),
    IllegalArgumentException("8","IllegalArgumentException"),
    TokenInvalidOrOverdueException("9", "Token invalid or overdue exception"),
    AuthorizationCodeError("10", "authorization code error"),
    WrongSignatureException("11","Wrong Signature Exception"),
    SystemException("50", "system Exception"),
    MissingServletRequestParameter("400","Missing servletRequest parameter"),
    TypeMismatchException("401","Request parameter Type not match"),
    RequestMethodNotAllowed("405","Request method  not Allowed"),
    ;
 
    private String code;
    private String msg;
 
    private ResultStatusCode(String code,String msg){
        this.code=code;
        this.msg=msg;
    }
 
    public String getMsg(){
        return this.msg;
    }
    public String getCode(){
        return this.code;
    }
 
}
 

4.自定义异常处理工具类

里面编写两个方法:

方法一:打印异常的详细信息

方法二:将枚举类型的状态码及描述转为json

注意:fastjson包,必须为1.2.24,否则没有configEnumAsJavaBean()方法。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.qiqi.exception.ResultStatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
 
/**
 * 返回异常的处理
 */
public class ExceptionUtil {
 
    private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);
 
    private ExceptionUtil() {
    }
 
    /**
     * 获取异常信息
     *
     * @param e 异常
     */
    public static String getErrorMessage(Exception e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            // 将出错的栈信息输出到printWriter中
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    logger.info(e1.getMessage());
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
        return sw.toString();
    }
    /**
     * 异常信息-->json
     *
     */
    public static String resultOf(ResultStatusCode resultStatusCode) {
        SerializeConfig config = new SerializeConfig();
        config.configEnumAsJavaBean(ResultStatusCode.class);
        return JSON.toJSONString(resultStatusCode, config);
    }
 
}

5.使用

Controller层:抛出异常让GlobalExceptionHandler捕获处理

注意:显示的throw异常后,后面的代码不再会被执行到。


    /**
     * 删除用户
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public Response deleteUser(@RequestParam(value = "user_id", required = true) String userId)
            throws UserNotFoundException {
        Response response = new Response();
        long id = Long.parseLong(userId);
        userService.deleteUser(id);
        response = new Response("0","删除用户成功",null);
        logger.info("删除用户:id="+ userId + "  result="+response.toString());
        return response;
    }

Service层: 抛出异常

    public void deleteUser(long userId )throws UserNotFoundException {
        if (userRepository.existsById(userId) == false){
             throw new UserNotFoundException();
        }
        userRepository.deleteById(userId);
    }

6.测试结果
一:填写请求参数时,故意让String类型的user_id =“3a",在long id = Long.parseLong(userId);转换时,会抛出IllegalArgumentException异常的子类NumberFormatException。由于我们在GlobalExceptionHandler捕获处理了IllegalArgumentException这个非法输入异常,并对其定义了状态码及描述。所以前端会收到json格式的响应:

{"code":"8","msg":"IllegalArgumentException"}

而在控制台,会输出日志:2019-01-04 14:39:21.437 ERROR com.qiqi.exception.GlobalExceptionHandler Line:118 - 非法输入:【For input string: "3a"】

二:请求时发送POST请求(代码中要求是GET请求)

前端响应:{"code":"405","msg":"Request method  not Allowed"}

后台日志:2019-01-04 14:41:41.963 ERROR com.qiqi.exception.GlobalExceptionHandler Line:58  - http请求的方法不正确:【Request method 'POST' not supported】

三:在程序中抛出IOException

IOException异常没有单独捕获,被捕获在allExceptionHandler方法中,在这个方法中打印出了异常的详细信息,包括出现异常的代码行号等,同时还打印了异常的堆栈信息。

前端响应:{"code":"50","msg":"system Exception"}
 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值