springboot自定义404,415错误处理(亲测可用,易懂)

  • 最近做一个项目,项目绝大多数请求都用的是ajax请求,但是,如果是4XX错误的话,springboot返回它自己的一套json,(全局异常处理是捕获不到这种错误的)如下:
{
  "timestamp": 1538032849685,
  "status": 415,
  "error": "Unsupported Media Type",
  "message": "Content type 'application/x-www-form-urlencoded' not supported",
  "path": "/account/login"
}
  • 我们知道,做rest api,我们的数据格式是统一的,而springboot给的我们如此格式,肯定不是我们想要的,我查了好多资料,发现说法云的雾的,好多纯属误导,我不知道他们写的时候,亲测过没有,真实放在项目中没有!
  • 所以决定自己动手造轮子,追寻源代码,我们会发现,上述json串是springboot里面一个BasicErrorController中的error方法返回的,而分析它的两个方法,一个error,一个errorHtml已经覆盖了所有的请求类型,因为你的请求要么是浏览器请求,要么不是,你想继承BasicErrorController复写方法?人家已经定义好返回类型,你怎么复写?然而,查资料我们会发现,如果有其他的ErrorController的实现,则默认的BasicErrorController将失去效果,如此,springboot就将这个错误处理的权利完全交给了程序猿,我们可以根据我们的具体需求,实现我们自己的ErrorController,其实BasicErrorController从某种意义上来说,是springboot提供给使用者的一个例子,告诉使用者,如果你想处理错误,应该怎么做!所以就自己处理了一个ErrorController,亲测完全可用,代码如下:
package com.rjhcsoft.credit.controller;

import com.rjhcsoft.credit.exceptions.BaseResponse;
import com.rjhcsoft.credit.exceptions.Status;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ErrorController extends AbstractErrorController {
    private final ErrorProperties errorProperties;

    public ErrorController(){
        this(new DefaultErrorAttributes(),new ErrorProperties());
    }

    /**
     * Create a new {@link ErrorController} instance.
     * @param errorAttributes the error attributes
     * @param errorProperties configuration properties
     */
    public ErrorController(ErrorAttributes errorAttributes,
                                ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.emptyList());
    }

    /**
     * Create a new {@link ErrorController} instance.
     * @param errorAttributes the error attributes
     * @param errorProperties configuration properties
     * @param errorViewResolvers error view resolvers
     */
    public ErrorController(ErrorAttributes errorAttributes,
                                ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    @Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    @RequestMapping(produces = "text/html")
    public ModelAndView errorHtml(HttpServletRequest request,
                                  HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView != null ? modelAndView : new ModelAndView("error", model));
    }

    @RequestMapping
    @ResponseBody
    public BaseResponse error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        Status sta = null;
        if (status.is1xxInformational()){
            sta=Status.INFORMATION_EXCEPTION;
        }else if (status.is3xxRedirection()){
            sta=Status.REDIRECT_ERROR;
        }else if (status.is4xxClientError()){
            sta=Status.CLIENT_ERROR;
        }else if (status.is5xxServerError()){
            sta=Status.SERVER_ERROR;
        }else sta=Status.UNKNKOWN_REQUEST;
        return BaseResponse.error(sta,body);
    }

    /**
     * Determine if the stacktrace attribute should be included.
     * @param request the source request
     * @param produces the media type produced (or {@code MediaType.ALL})
     * @return if the stacktrace attribute should be included
     */
    protected boolean isIncludeStackTrace(HttpServletRequest request,
                                          MediaType produces) {
        ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
        if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
            return true;
        }
        if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
            return getTraceParameter(request);
        }
        return false;
    }

    /**
     * Provide access to the error properties.
     * @return the error properties
     */
    protected ErrorProperties getErrorProperties() {
        return this.errorProperties;
    }

}

  • 注意:其中的error方法的返回值BaseResponse是我自己定义的一套ajax返回格式,具体情况具体对待,其中Status是我们自己定义的一套状态码,是枚举,这个大家也是具体情况具体对待
  • 最后的返回结果如下:
{
  "code": "1005",
  "data": {
    "timestamp": 1538033789395,
    "status": 415,
    "error": "Unsupported Media Type",
    "message": "Content type 'application/x-www-form-urlencoded' not supported",
    "path": "/account/login"
  },
  "msg": "客户端请求错误,4XX错误"
}
  • 大家发现,最后的返回结果已经是我们自己定义的格式了,至于全局异常处理网上有很多,这里我也贴一份我定义的
package com.rjhcsoft.credit.exceptions.resolve;

import com.alibaba.fastjson.JSON;
import com.rjhcsoft.credit.exceptions.BaseException;
import com.rjhcsoft.credit.exceptions.BaseResponse;
import com.rjhcsoft.credit.exceptions.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class GlobalExceptionResolve implements HandlerExceptionResolver {
    private static final Logger EXCEPTION = LoggerFactory.getLogger("exception");

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        response.setStatus(200);
        BaseResponse baseResponse = parseException(ex);
        if (isAjax(request)) return returnJson(baseResponse,response);
        else return returnView(baseResponse,response);
    }

    /**
     * 解析异常,这里做简单解析
     * @param ex
     * @return
     */
    private BaseResponse parseException(Exception ex){
        EXCEPTION.error(ex.getMessage());
        if (ex instanceof BaseException){
            Exception e = ((BaseException) ex).getE();
            EXCEPTION.error(e==null?"":e.getMessage()==null?"":e.getMessage());
            return BaseResponse.error(((BaseException) ex).getStatus());
        }else return BaseResponse.error(Status.UNKNKOWN);
    }

    /**
     * 返回json串
     * @param baseResponse
     * @param response
     * @return
     */
    public static ModelAndView returnJson(BaseResponse baseResponse,HttpServletResponse response){
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        try(PrintWriter writer = response.getWriter()) {
            writer.print(JSON.toJSONString(baseResponse));
        }catch (IOException e){
            e.printStackTrace();
        }
        return new ModelAndView();
    }

    /**
     * 返回页面
     * @param baseResponse
     * @param response
     * @return
     */
    public static ModelAndView returnView(BaseResponse baseResponse,HttpServletResponse response){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("/error");
        modelAndView.addObject("response",baseResponse);
        return modelAndView;
    }

    /**
     * 判定师傅是ajax请求
     * @param request
     * @return
     */
    public static boolean isAjax(HttpServletRequest request){
        String requestType = request.getHeader("X-Requested-With");
        if("XMLHttpRequest".equals(requestType)){
            return true;
        }else{
            return false;
        }
    }
}
  • BaseException
package com.rjhcsoft.credit.exceptions;

public class BaseException extends RuntimeException{
    private Status status;
    private Exception e;
    protected BaseException(Status status){
        super(status.json());
        this.status=status;
    }

    protected BaseException(Status status,Exception e){
        this(status);
        this.e = e;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public Exception getE() {
        return e;
    }

    public void setE(Exception e) {
        this.e = e;
    }
}

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页