SpringBoot项目如何处理全局异常

【前言】

前几天晚上收到了API网关我接口异常报警。我就去看了看我们的接口日志发现没有任何异常就感觉很神奇,然后找API网关的相关人员查了下日志(如下),发现这个日志是爬虫导致的并且我们系统并没有显示,发现这个异常并不是接口里的异常而且在Controller层之前的异常。所以要将全局异常处理下方便以后排查定位问题

【全局异常处理】

SpringBoot中有在个注解@ControllerAdvice这个注解是spring在3.2新增加的controller的增强器,其作用

  1. 全局异常处理(通常和@ExceptionHandler 一起使用)

  2. 全局数据绑定(通常和@ModelAttribute一起使用)

  3. 全局数据预处理(通常和@InitBinder一起使用)

这次主要讲解@ControllerAdvice@ExceptionHandler来处理一些异常问题和一些我的个人小见解

【示例一】

  • 【1.1查询接口示例代码】

来咱们先演示下咱们springBoot中的controller的一些异常,来先上代码

package com.example.demo.controller;
​
import com.example.demo.common.JsonResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
​
import java.util.Map;
​
/**
 *  测试
 * @author shuaiTuTu
 */
@RestController
@RequestMapping("/test")
public
class TestController {
​
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public Object getAgentInfo(
            @RequestParam(value = "id",required = true) Integer id,
            @RequestParam(value = "name") String name
    ) {
        return new JsonResponse(id);
    }
}
​

正常查询结果

如此加入错误id=a,查询结果

错误日志

2022-04-10 17:30:58.055  WARN 104916 --- [nio-8080-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "a"]
  • 【1.2异常处理示例代码】

异常处理就需要咱们出捕获处理了,来先上代码,@ExceptionHandler可以处理各种指定异常以及自定义异常,大家可以根据自己需要处理

package com.example.demo.common;
​
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
​
/**
 * 处理异常
 * @author shuaiTuTu
 */
@ControllerAdvice
public class CustomControllerAdvice {
​
    protected static final Logger logger = LoggerFactory.getLogger(CustomControllerAdvice.class);
​
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseBody
    public String missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
        logger.error("缺少参数异常");
        return "缺少参数异常"+ex.getMessage();
    }
​
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public String exceptionHandler(Exception ex) {
        logger.error("发送异常");
        return "异常"+ex.getMessage();
    }
​
}

这次异常处理结果

虽然问题咱们处理了,但是给人的感觉很不好,也就是不够人性化。

【处理方式二】

  • 【2.1定义基础返回结果】

由于之前返回结果不统一,报错的处理不友好咱们,增加统一的处理

package com.example.demo.common;
​
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
​
/**
 *  返回结果
 * @author shuaiTuTu
 * @date 2018-10-30
 *
 */
public class JsonResponse {
​
    private JsonResponseHeader header;
    private Object body;
​
    public
    JsonResponse() {
        this(ResponseStatusEnum.OK.status, ResponseStatusEnum.OK.msg, null);
    }
​
    public
    JsonResponse(int status) {
        this(status, ResponseStatusEnum.getEnum(status).msg, null);
    }
​
    public
    JsonResponse(int status, String msg) {
        this(status, msg,null);
    }
​
    public
    JsonResponse(Object body) {
        this(ResponseStatusEnum.OK.status, ResponseStatusEnum.OK.msg, body);
    }
​
    public
    JsonResponse(int status, String msg, Object body) {
        this.header = new JsonResponseHeader(status, msg);
        if (body == null) {
            body = new HashMap();
        }
        this.body = body;
    }
​
    public
    JsonResponse(String key, Object value) {
        Map<String, Object> bodyObj = new HashMap<>();
        bodyObj.put(key, value);
​
        this.header = new JsonResponseHeader(ResponseStatusEnum.OK.status, ResponseStatusEnum.OK.msg);
        this.body = bodyObj;
    }
​
    public JsonResponseHeader getHeader() {
        return header;
    }
​
    public void setHeader(JsonResponseHeader header) {
        this.header = header;
    }
​
    public Object getBody() {
        return body;
    }
​
    public void setBody(Object body) {
        this.body = body;
    }
​
    /**
     * 业务层 响应头对象
     */
    public class JsonResponseHeader {
​
        private int status;
        private String msg;
​
        public JsonResponseHeader(int status, String msg) {
            this.status = status;
            this.msg = msg;
        }
​
        public int getStatus() {
            return status;
        }
​
        public void setStatus(int status) {
            this.status = status;
        }
​
        public String getMsg() {
            return msg;
        }
​
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
​
    /**
     * 响应状态枚举
     */
    public enum ResponseStatusEnum {
​
        NULL(0, ""),
        OK(200, "OK"),
        INVALID_SESSION(400, "Request fail: invalid session"),
        UNAUTHORIZED(401, "Request fail: unauthorized"),
        ERROR(500, "Request fail: unexpected error"),
        ACCOUNT_EXPIRED_FORMAL(501, "Request fail: formal account expired"),
        ACCOUNT_EXPIRED_TRIAL(502, "Request fail: trial account expired"),
        ACCOUNT_IS_STOP(503, "Request fail: account is stop"),
        SESSION_EXPIRED(504, "Request fail: session expired"),
        INVALID_PARAMETER(600, "Request fail: invalid parameter"),
        INVALID_SIGN(601, "Request fail: invalid sign"),
        REQUEST_EXPIRED(602, "Request expired"),
        REQUEST_MISS(603, "Request missing"),
        INVALID_PARAMETER_COUNT(603, "Request fail: 无效的请求数量值"),
        INVALID_PARAMETER_TIME(604, "Request fail: 无效的时间范围参数"),
        PARAMETER_IS_NOT_PRESENT(605, "Request fail: parameter is not present"),
        SMS_SEND_ERROR(606, "sms send failed"),
        SMS_SENDER_IS_EMPTY(607, "sms receivers is empty"),
        ADD_EVENT_FAILED(608, "add or update or delete event failed");
​
​
        private int status;
        private String msg;
​
        ResponseStatusEnum(int status, String msg) {
            this.status = status;
            this.msg = msg;
        }
​
        public int getStatus() {
            return status;
        }
​
        public String getMsg() {
            return msg;
        }
​
        public static ResponseStatusEnum getEnum(int status) {
            Optional<ResponseStatusEnum> optional = Arrays.stream(ResponseStatusEnum.values())
                    .filter(t -> t.getStatus() == status)
                    .findFirst();
            return optional.orElse(NULL);
        }
​
    }
​
}
​
  • 【2.2统一处理异常和特殊处理】

    这次在全局处理上增加了入参:HttpServletRequest这个可以获取请求uri方法做些监控处理

    package com.example.demo.common;
    ​
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.HttpRequest;
    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;
    ​
    import javax.servlet.http.HttpServletRequest;
    import java.net.URI;
    ​
    /**
     * 处理异常
     * @author shuaiTuTu
     */
    @ControllerAdvice
    public class CustomControllerAdvice {
    ​
        protected static final Logger logger = LoggerFactory.getLogger(CustomControllerAdvice.class);
    ​
        @ExceptionHandler(value = MissingServletRequestParameterException.class)
        @ResponseBody
        public JsonResponse missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
            logger.error("缺少参数异常");
            return new JsonResponse(JsonResponse.ResponseStatusEnum.REQUEST_MISS.getStatus(), ex.getMessage());
        }
    ​
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public JsonResponse exceptionHandler(HttpServletRequest request, Exception ex) {
            String uri = request.getRequestURI();
    ​
            logger.error(uri+"发送异常");
            return new JsonResponse(JsonResponse.ResponseStatusEnum.ERROR.getStatus(),"uri:" +uri+" EX: "+ ex.getMessage());
        }
    ​
    }
    ​

  • 【2.4结果展示】

总结

  1. 统一的异常处理,可以减少代码的重复和复杂度,有利于代码维护,快速定位问题

  2. 利用异常处理作出合理的监控方法提前发现问题

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值