四时宝库

程序员的知识宝库

SpringBoot 应用搭建之始终返回统一的JSON格式

SpringBoot 应用搭建之始终返回统一的JSON格式

之前的一篇文章:SpringBoot 应用搭建之全局异常处理要这样搞

有网友进行了尝试,留言说404的怎么处理?

上篇文章中的处理方法确实只能保证进入我们的Controller之后的异常都进行捕获,并返回统一的格式。

但是在没进入Controller前的一些异常是无法捕获的。比如404,400、还有一些500。

关于这些错误的处理方法大概有下面这些:

方式一、前端统一处理

其实对于这些错误,不建议后端全部拦截,使用SpringBoot框架的应用应该都进行了前后端分离。可以在前端统一进行网络请求的拦截处理,如果返回的http status 不是200,前端对于不同的status进行相应的处理即可。

方式二、对于404的处理

我们可以在Springboot的配置中设置404时抛出异常。这样我们就可以在GlobalExceptionInterceptor类中进行捕获了。

配置:

# 出现404错误时, 直接抛出异常 
spring.mvc.throw-exception-if-no-handler-found=true 
# 不要对资源文件建立映射 
spring.resources.add-mappings=false

然后我们直接在 类中捕获该类型的异常即可:

package com.xtoad.study.common.web.interceptor;

import com.xtoad.study.common.web.ResultDTO;
import com.xtoad.study.common.web.exception.BusinessException;
import com.xtoad.study.common.web.exception.ResultCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常捕捉处理
 *
 * @author xtoad
 * @date 2021/02/18
 */
@RestControllerAdvice
public class GlobalExceptionInterceptor {

    private final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionInterceptor.class);

    /**
     * 全局业务异常捕捉处理
     *
     * @param ex 业务异常
     * @return 结果
     */
    @ExceptionHandler(value = BusinessException.class)
    public ResultDTO businessExceptionHandler(BusinessException ex) {
        LOGGER.info(ex.getMsg(), ex);
        return ResultDTO.failed(ex.getCode(), ex.getMsg());
    }

    /**
     * 全局异常捕捉处理
     *
     * @param ex Exception
     * @return 结果
     */
    @ExceptionHandler(value = Exception.class)
    public ResultDTO exceptionHandler(Exception ex) {
        LOGGER.error(ex.getMessage(), ex);
        if (ex instanceof MethodArgumentNotValidException) {
            return ResultDTO.failed(ResultCodeEnum.INVALID_PARAMETER);
        } else if (ex instanceof org.springframework.web.servlet.NoHandlerFoundException) {
            // 这里判断异常是404时
            return ResultDTO.failed("404", "请求的url不存在");
        } else {
            return ResultDTO.failed(ResultCodeEnum.UNKNOW_ERROR);
        }
    }
}

方式三、重写ErrorController,任何时候都返回统一格式

上面方式二是能处理404了,但是也不能映射静态资源了,而且开篇我们说了,除了404,还有他的一些异常,如何捕获呢?

事实上,SpringBoot 在处理异常的时候,500/404默认都会转发到/error这个url下,而这个异常的处理类是ErrorController。

所以我们重写ErrorController:

@RestController
public class GlobalExceptionController extends AbstractErrorController {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionController.class);
    private static final String ERROR_PATH = "/error";
 
    public GlobalExceptionController(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }
 
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
 
    @RequestMapping(value = ERROR_PATH)
    public ResultDTO error(HttpServletRequest request) {
        WebRequest webRequest = new ServletWebRequest(request);
        Throwable e = getError(webRequest);
        if (e == null) {
            Map<String, Object> attributes = getErrorAttributes(request, false);
            Object timestamp = attributes.get("timestamp");
            Object status = attributes.get("status");
            String error = attributes.get("error").toString();
            Object path = attributes.get("path");
            LOGGER.error("status {} error {} path{} timestamp {}", status, error, path, timestamp);
            return ResultDTO.failed(Integer.parseInt(status.toString()), error);
        }
        if (e instanceof TokenExpiredException) {
            TokenExpiredException tokenExpiredException = (TokenExpiredException) e;
            return ResultDTO.failed(tokenExpiredException.getHttpStatus().value(), tokenExpiredException.getHttpStatus().getReasonPhrase());
        } else if (e instanceof CodeException) {
            CodeException codeException = (CodeException) e;
            String message = e.getMessage();
            if (StringUtils.isEmpty(message)) {
                message = String.format("[%s][%s]", codeException.getErrCode(), codeException.getErrMsg());
            }
            return ResultDTO.failed(codeException.getErrCode(), message);
        } else {
            return ResultDTO.failed("500", "系统繁忙,请稍后再试");
        }
    }
 
    private Throwable getError(WebRequest webRequest) {
        return (Throwable) this.getAttribute(webRequest, "javax.servlet.error.exception");
    }
 
    private Object getAttribute(RequestAttributes requestAttributes, String name) {
        return requestAttributes.getAttribute(name, 0);
    }
}

该方式,比较简单粗暴,除非你确定你的应用无论什么场合都返回统一的JSON,不否不建议使用。

更推荐方式一,前端统一拦截,对http status 进行判断处理。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接