美文网首页
6.从零搭建WebApi接口开发框架-公共异常拦截

6.从零搭建WebApi接口开发框架-公共异常拦截

作者: 孔垂云 | 来源:发表于2017-12-24 23:47 被阅读0次

实际开发中,需要对各种异常进行处理,并记录日志,常见的异常包括以下几种:

  • 系统错误,最常见的,系统未处理的
  • 404,请求url不存在
  • 参数错误,请求的参数不符合校验规则

在springBoot中采用controllerAdvice来进行公共异常的拦截。先设置系统的日志,用于记录异常信息。

logback设置

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <!-- appender -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %m%n
            </pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 系统日志配置 -->
    <appender name="sysLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/sys.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/sys-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
        </encoder>
    </appender>
    <appender name="daoLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/dao.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/dao-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>[%d{yyyyMMdd HH:mm:ss}] [%-5level] [%-30logger{0}][%-3L]
                [SeqId:%X{SeqId}] - %m%n
            </Pattern>
        </encoder>
    </appender>
    <appender name="serviceLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/service.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/service-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
        </encoder>
    </appender>
    <appender name="controllerLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/controller.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/controller-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>[%d{yyyyMMdd HH:mm:ss}]- %m%n</Pattern>
        </encoder>
    </appender>

    <appender name="operationLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/operation.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/operation-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>%m%n</Pattern>
        </encoder>
    </appender>
    <appender name="performanceLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/performance.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/performance-%d{yyyyMMdd}.log
            </FileNamePattern>
            <maxHistory>365</maxHistory>
        </rollingPolicy>
        <encoder>
            <ImmediateFlush>true</ImmediateFlush>
            <charset>utf-8</charset>
            <Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
        </encoder>
    </appender>
    <!-- additivity为false不向root控制台输出 -->
    <logger name="sysLog" additivity="false" level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="sysLogAppender"/>
    </logger>
    <logger name="daoLog" additivity="false" level="ERROR">
        <appender-ref ref="stdout"/>
        <appender-ref ref="daoLogAppender"/>
    </logger>
    <logger name="serviceLog" additivity="false" level="ERROR">
        <appender-ref ref="stdout"/>
        <appender-ref ref="serviceLogAppender"/>
    </logger>
    <logger name="controllerLog" additivity="false" level="INFO">
        <appender-ref ref="stdout"/>
        <appender-ref ref="controllerLogAppender"/>
    </logger>
    <logger name="operationLog" additivity="false" level="INFO">
        <appender-ref ref="stdout"/>
        <appender-ref ref="operationLogAppender"/>
    </logger>
    <logger name="performanceLog" additivity="false" level="INFO">
        <appender-ref ref="stdout"/>
        <appender-ref ref="performanceLogAppender"/>
    </logger>

    <!-- root 默认日志配置 -->
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="sysLogAppender"/>
    </root>
    <logger name="org.springframework" level="INFO"/>
</configuration>

和正常的logback日志日志没有什么不同,把日志分为以下几类:

  • sys:记录工程启动和停止的日志
  • controller:记录controller层的异常
  • service:service层异常,主要是用AOP来记录
  • dao:dao层的异常,主要是用AOP来记录
  • performace:系统性能监测记录,也是用AOP来记录
  • operation:操作记录,主要记录接口请求记录

controllerAdvice

@ControllerAdvice
public class ApiControllerAdvice {
    /**
     * 定义日志处理
     */
    private static Logger logger = LoggerFactory.getLogger("controllerLog");

    /**
     * 系统异常处理,比如:404,500
     *
     * @param request  request
     * @param response response
     * @param e        异常
     * @return JsonResult结构
     * @throws Exception 异常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public JsonResult defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) throws Exception {
        logger.error("系统异常", e);
        logException(request);
        if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return new JsonResult(false, GlobalReturnCode.SYSTEM_PATH_NOEXIST);
        } else {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            return new JsonResult(false, GlobalReturnCode.SYSTEM_ERROR);
        }
    }

    /**
     * 取得header的所有属性
     *
     * @param headers 请求的headers
     * @param request request
     * @return 把header拼成字符串
     */
    private String getHeaderValue(Enumeration<String> headers, HttpServletRequest request) {
        String str = "";
        while (headers.hasMoreElements()) {
            String header = headers.nextElement();
            str += header + "=" + request.getHeader(header) + "&";
        }
        return str;
    }

    /**
     * 打印所有异常信息
     *
     * @param request request
     */
    private void logException(HttpServletRequest request) {
        try {
            logger.error("请求路径:" + request.getServletPath());
            logger.error("请求参数:" + request.getParameterMap().toString());
            logger.error("请求header:" + getHeaderValue(request.getHeaderNames(), request));
            logger.error("请求body:" + StringUtil.getBodyString(request.getReader()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里先记录404错误和500错误,如果接口请求有异常,直接返回对应的json串

404错误
为了使springBoot能拦截404错误,需要在application.yml中增加如下配置:

spring:
  mvc:
    throw-exception-if-no-handler-found: true
  resources:
    add-mappings: false

如果不加上面配置,404是无法获取拦截的。
如果没有配置异常拦截,直接请求springboot会返回如下错误:


404默认返回.png

配置后返回如下信息:


配置后返回.png

500错误

为了整体拦截系统异常,需要定制返回的500错误,避免返回异常给调用端。如下图所示:


系统异常.png

这里讲完了如何拦截404和500错误,下一节会讲解拦截参数非法错误。

源码下载

本例子详细源码

相关文章

网友评论

      本文标题:6.从零搭建WebApi接口开发框架-公共异常拦截

      本文链接:https://www.haomeiwen.com/subject/iyhtixtx.html