springboot全局异常处理

左心房为你撑大大i 提交于 2019-12-24 02:46:02

基于springboot的全局异常处理 

 

1 编写ResultBuilder类


package com.test.domi.common.utils;import org.apache.commons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.autoconfigure.web.ErrorProperties;import org.springframework.boot.autoconfigure.web.ServerProperties;import org.springframework.core.Ordered;import org.springframework.http.HttpStatus;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.servlet.HandlerExceptionResolver;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.List;public class ResultBuilder implements HandlerExceptionResolver,Ordered {    private static final Logger LOGGER = LoggerFactory.getLogger(ResultBuilder.class);    private static final String ERROR_NAME = "fp.error";    private ErrorProperties errorProperties;    public ErrorProperties getErrorProperties() {        return errorProperties;    }    public ResultBuilder(ServerProperties serverProperties){        LOGGER.info("serverProperties:{}",serverProperties.getError());        this.errorProperties = serverProperties.getError();    }    public ResultInfo getErrorInfo(HttpServletRequest request){        return  this.getErrorInfo(request,this.getError(request));    }    /**     * 全局异常返回处理     * @param request     * @param error     * @return     */    public ResultInfo getErrorInfo(HttpServletRequest request,Throwable error){          ResultInfo resultInfo = new ResultInfo();          //根据不同的error获取错误信息          String resultCode = "";          StringBuffer msg = new StringBuffer();          if (error instanceof MethodArgumentNotValidException) {              //1 参数校验异常              resultCode = getString2((MethodArgumentNotValidException) error, resultCode, msg);          }else {              //3 httpStatu枚举code对应的异常              resultCode = getString3(request, msg);          }          resultInfo.setCode(resultCode);          resultInfo.setMessage(msg.toString());          resultInfo.setData((Object)null);          return resultInfo;    }    private String getString3(HttpServletRequest request, StringBuffer msg) {        msg.append(this.getHttpStatus(request).getReasonPhrase());        return String.valueOf(this.getHttpStatus(request).value());    }    private String getString2(MethodArgumentNotValidException error, String resultCode, StringBuffer msg) {        BindingResult bindingResult = error.getBindingResult();        if (bindingResult.hasErrors()) {            List<FieldError> list = bindingResult.getFieldErrors();            resultCode =ResultCode.CONNECT_ERROR.getCode();            for (FieldError fieldError : list) {                msg.append(fieldError.getDefaultMessage() + ";");            }        }        return resultCode;    }    private String getString(Throwable error, StringBuffer msg) {        msg.append(error.getMessage());        return ResultCode.INSERT_ERROR.getCode();    }    /**     *  拿到最根部的error,携带手动抛出的异常信息     * @param request     * @return     */    public Throwable getError(HttpServletRequest request){        Throwable error = (Throwable)request.getAttribute(ERROR_NAME);        if (error == null) {            error = (Throwable)request.getAttribute("javax.servlet.error.exception");        }        if (error != null) {            //while (error instanceof ServletException && ((Throwable) error).getCause() != null) {            while (error instanceof Exception && ((Throwable) error).getCause() != null) {                error = ((Throwable) error).getCause();            }        } else {            String message = (String)request.getAttribute("javax.servlet.error.message");            if (StringUtils.isNotEmpty(message)) {                HttpStatus status = this.getHttpStatus(request);                message = "Unknown Exception With" + status.value() + " " + status.getReasonPhrase();            }            error = new Exception(message);        }        return (Throwable)error;    }    public HttpStatus getHttpStatus(HttpServletRequest request){        Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");        try {            return statusCode != null ? HttpStatus.valueOf(statusCode.intValue()) : HttpStatus.INTERNAL_SERVER_ERROR;        } catch (Exception var4) {            return HttpStatus.INTERNAL_SERVER_ERROR;        }    }    @Override    public int getOrder() {        return 0;    }    @Override    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {        httpServletRequest.setAttribute(ERROR_NAME, e);        return null;    }}

 

2 编写ExceptionConfig类(传入ServerProperties ,实例化ResultBuilder。springboot中ErrorProperties类定义了异常自动映射路径@Value("${error.path:/error}")private String path = "/error"

package com.test.domi.config;

import com.test.domi.common.system.ResultBuilder;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.annotation.Resource;

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionConfig {
    @Resource
    private ServerProperties serverProperties;
    @Bean
    public ResultBuilder resultBuilder(){
        return  new ResultBuilder(serverProperties);
    }
}

ErrorProperties:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure.web;

import org.springframework.beans.factory.annotation.Value;

public class ErrorProperties {
    @Value("${error.path:/error}")
    private String path = "/error";
    private ErrorProperties.IncludeStacktrace includeStacktrace;

    public ErrorProperties() {
        this.includeStacktrace = ErrorProperties.IncludeStacktrace.NEVER;
    }

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public ErrorProperties.IncludeStacktrace getIncludeStacktrace() {
        return this.includeStacktrace;
    }

    public void setIncludeStacktrace(ErrorProperties.IncludeStacktrace includeStacktrace) {
        this.includeStacktrace = includeStacktrace;
    }

    public static enum IncludeStacktrace {
        NEVER,
        ALWAYS,
        ON_TRACE_PARAM;

        private IncludeStacktrace() {
        }
    }
}

 

3 定义全局 异常Controller接管所有抛出的异常

package spring.cloud.common.controller;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;import org.springframework.boot.web.servlet.error.ErrorController;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.ModelAndView;import spring.cloud.common.util.ResultBuilder;import spring.cloud.common.util.ResultInfo;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;@RestController@RequestMapping("/error")public class GlobalErrorController implements ErrorController{    /**     * 1 ErrorController 接口的默认实现类是abstract:AbstractErrorController     * 2 AbstractErrorController 的子类 BasicErrorController 才是真正干活儿的实现类(分html、json 两个方法处理,我们只需要在GlobalErrorController重写这2个方法即可)     * 3 BasicErrorController 有 private final ErrorProperties errorProperties;属性     * ErrorProperties里面记录了error的路径:     * @Value("${error.path:/error}")     * private String path = "/error";     * -----     * 4 BasicErrorController 的封装只能将状态码的提示信息返回前台,不能拿到手动抛异常的信息,因此需要实现HandlerExceptionResolver     *  ------------------------------------------------------------     *  BasicErrorController只有有参构造,无法直接继承     *  如果不实现ErrorController,则会造成相同路径/error有2个类,冲突了。启动时报如下异常:     *  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'basicErrorController' method     * public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)     * to {[/error],produces=[text/html]}: There is already 'globalErrorController' bean method     */    private final static String DEFAULT_ERROR_VIEW = "/error";    private final static  org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GlobalErrorController.class);    /**     * ResultBuilder 实现 HandlerExceptionResolver 接口重写public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e)     * 通过httpServletRequest.setAttribute("fp.error", e);将Exception放到request中     * 这种方法的好处是能拿到手动抛异常的信息     */    @Resource    private ResultBuilder resultBuilder;    /** 1- BasicErrorController只有有参构造,无法直接继承,     *  2- 如果不实现ErrorController,则会造成相同路径/error有2个类,冲突了。启动时报如下异常:     *  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'basicErrorController' method     * public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)     * to {[/error],produces=[text/html]}: There is already 'globalErrorController' bean method     * ----------------------     * ErrorProperties里面记录了error的路径:     * * @Value("${error.path:/error}")     * * private String path = "/error";     * 如果不需要从GlobalErrorController中的getErrorPath方法获取该路径,则该方法可以空实现     */    @Override    public String getErrorPath(){//        return null;        return  resultBuilder.getErrorProperties().getPath();    }    /**     * 如果请求头返回的类型是text/html,则返回到错误信息页面     * @param request     * @return     */    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)    public ModelAndView errorHtml(HttpServletRequest request) {        return new ModelAndView(DEFAULT_ERROR_VIEW,"errorInfo",resultBuilder.getErrorInfo(request));    }    /**     * 除了text/html的请求头信息,其它都返回json格式     * @param request 请求对象     * @return 错误信息字符串     */    @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})    public ResultInfo error(HttpServletRequest request){        return resultBuilder.getErrorInfo(request);    }}

 

 

配置完毕,后台的未被捕获的异常将从dao层到dervice层到controller层,然后被全局异常controller统一接管,封装之后返回给前台!

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!