Commit 70af2bc78c1dfbf516f6a8589597b251aac8466a

Authored by YunaiV
1 parent fe94e459

1. 增加 API 访问日志、异常日志表

2. 调整过滤器的顺序
ruoyi-common/src/main/resources/logback.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<configuration>
3   - <!-- 日志存放路径 -->
4   - <property name="log.path" value="/home/ruoyi/logs" />
5   - <!-- 日志输出格式 -->
6   - <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
7   -
8   - <!-- 控制台输出 -->
9   - <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
10   - <encoder>
11   - <pattern>${log.pattern}</pattern>
12   - </encoder>
13   - </appender>
14   -
15   - <!-- 系统日志输出 -->
16   - <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
17   - <file>${log.path}/sys-info.log</file>
18   - <!-- 循环政策:基于时间创建日志文件 -->
19   - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
20   - <!-- 日志文件名格式 -->
21   - <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
22   - <!-- 日志最大的历史 60天 -->
23   - <maxHistory>60</maxHistory>
24   - </rollingPolicy>
25   - <encoder>
26   - <pattern>${log.pattern}</pattern>
27   - </encoder>
28   - <filter class="ch.qos.logback.classic.filter.LevelFilter">
29   - <!-- 过滤的级别 -->
30   - <level>INFO</level>
31   - <!-- 匹配时的操作:接收(记录) -->
32   - <onMatch>ACCEPT</onMatch>
33   - <!-- 不匹配时的操作:拒绝(不记录) -->
34   - <onMismatch>DENY</onMismatch>
35   - </filter>
36   - </appender>
37   -
38   - <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
39   - <file>${log.path}/sys-error.log</file>
40   - <!-- 循环政策:基于时间创建日志文件 -->
41   - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
42   - <!-- 日志文件名格式 -->
43   - <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
44   - <!-- 日志最大的历史 60天 -->
45   - <maxHistory>60</maxHistory>
46   - </rollingPolicy>
47   - <encoder>
48   - <pattern>${log.pattern}</pattern>
49   - </encoder>
50   - <filter class="ch.qos.logback.classic.filter.LevelFilter">
51   - <!-- 过滤的级别 -->
52   - <level>ERROR</level>
53   - <!-- 匹配时的操作:接收(记录) -->
54   - <onMatch>ACCEPT</onMatch>
55   - <!-- 不匹配时的操作:拒绝(不记录) -->
56   - <onMismatch>DENY</onMismatch>
57   - </filter>
58   - </appender>
59   -
60   - <!-- 用户访问日志输出 -->
61   - <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
62   - <file>${log.path}/sys-user.log</file>
63   - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
64   - <!-- 按天回滚 daily -->
65   - <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
66   - <!-- 日志最大的历史 60天 -->
67   - <maxHistory>60</maxHistory>
68   - </rollingPolicy>
69   - <encoder>
70   - <pattern>${log.pattern}</pattern>
71   - </encoder>
72   - </appender>
73   -
74   - <!-- 系统模块日志级别控制 -->
75   - <logger name="com.ruoyi" level="info" />
76   - <!-- Spring日志级别控制 -->
77   - <logger name="org.springframework" level="warn" />
78   -
79   - <root level="info">
80   - <appender-ref ref="console" />
81   - </root>
82   -
83   - <!--系统操作日志-->
84   - <root level="info">
85   - <appender-ref ref="file_info" />
86   - <appender-ref ref="file_error" />
87   - </root>
88   -
89   - <!--系统用户操作日志-->
90   - <logger name="sys-user" level="info">
91   - <appender-ref ref="sys-user"/>
92   - </logger>
93   -</configuration>
94 0 \ No newline at end of file
src/main/java/cn/iocoder/dashboard/common/enums/UserTypeEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.common.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 全局用户类型枚举
  8 + */
  9 +@AllArgsConstructor
  10 +@Getter
  11 +public enum UserTypeEnum {
  12 +
  13 + MEMBER(1, "会员"), // 面向 c 端,普通用户
  14 + ADMIN(2, "管理员"); // 面向 b 端,管理后台
  15 +
  16 + /**
  17 + * 类型
  18 + */
  19 + private final Integer value;
  20 + /**
  21 + * 类型名
  22 + */
  23 + private final String name;
  24 +
  25 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/config/ApiLogConfiguration.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.config;
  2 +
  3 +import cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter;
  4 +import cn.iocoder.dashboard.framework.web.config.WebProperties;
  5 +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum;
  6 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  7 +import org.springframework.context.annotation.Bean;
  8 +import org.springframework.context.annotation.Configuration;
  9 +
  10 +import javax.servlet.Filter;
  11 +
  12 +@Configuration
  13 +public class ApiLogConfiguration {
  14 +
  15 + /**
  16 + * 创建 ApiAccessLogFilter Bean,记录 API 请求日志
  17 + */
  18 + @Bean
  19 + public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties) {
  20 + ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties);
  21 + return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER);
  22 + }
  23 +
  24 + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
  25 + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
  26 + bean.setOrder(order);
  27 + return bean;
  28 + }
  29 +
  30 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/filter/ApiAccessLogFilter.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.filter;
  2 +
  3 +import cn.hutool.extra.servlet.ServletUtil;
  4 +import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO;
  5 +import cn.iocoder.dashboard.framework.web.config.WebProperties;
  6 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
  7 +import lombok.AllArgsConstructor;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.web.filter.OncePerRequestFilter;
  10 +
  11 +import javax.servlet.FilterChain;
  12 +import javax.servlet.ServletException;
  13 +import javax.servlet.http.HttpServletRequest;
  14 +import javax.servlet.http.HttpServletResponse;
  15 +import java.io.IOException;
  16 +import java.util.Date;
  17 +import java.util.Map;
  18 +
  19 +/**
  20 + * API 访问日志 Filter
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@AllArgsConstructor
  25 +@Slf4j
  26 +public class ApiAccessLogFilter extends OncePerRequestFilter {
  27 +
  28 + private final WebProperties webProperties;
  29 +
  30 + @Override
  31 + protected boolean shouldNotFilter(HttpServletRequest request) {
  32 + return !request.getRequestURI().startsWith(webProperties.getApiPrefix());
  33 + }
  34 +
  35 + @Override
  36 + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  37 + throws ServletException, IOException {
  38 + // 获得开始时间
  39 + Date startTime = new Date();
  40 + // 提前获得参数,避免 XssFilter 过滤处理
  41 + Map<String, String> queryString = ServletUtil.getParamMap(request);
  42 + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null;
  43 +
  44 + try {
  45 + // 继续过滤器
  46 + filterChain.doFilter(request, response);
  47 + // 正常执行,记录日志
  48 + createApiAccessLog(request, startTime, queryString, requestBody, null);
  49 + } catch (Exception ex) {
  50 + // 异常执行,记录日志
  51 + createApiAccessLog(request, startTime, queryString, requestBody, ex);
  52 + throw ex;
  53 + }
  54 + }
  55 +
  56 + private void createApiAccessLog(HttpServletRequest request, Date startTime,
  57 + Map<String, String> queryString, String requestBody, Exception ex) {
  58 + try {
  59 + ApiAccessLogCreateDTO createDTO = this.buildApiAccessLogDTO(request, startTime, queryString, requestBody, ex);
  60 +
  61 + } catch (Exception e) {
  62 + log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex);
  63 + }
  64 + }
  65 +
  66 + private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime,
  67 + Map<String, String> queryString, String requestBody, Exception ex) {
  68 + return null;
  69 + }
  70 +
  71 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/dto/ApiAccessLogCreateDTO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.service.dto;
  2 +
  3 +import javax.validation.constraints.NotNull;
  4 +import java.util.Date;
  5 +
  6 +/**
  7 + * API 访问日志创建 DTO
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class ApiAccessLogCreateDTO {
  12 +
  13 + /**
  14 + * 链路追踪编号
  15 + */
  16 + private String traceId;
  17 + /**
  18 + * 用户编号
  19 + */
  20 + private Integer userId;
  21 + /**
  22 + * 用户类型
  23 + */
  24 + private Integer userType;
  25 + /**
  26 + * 应用名
  27 + */
  28 + @NotNull(message = "应用名不能为空")
  29 + private String applicationName;
  30 +
  31 + /**
  32 + * 请求方法名
  33 + */
  34 + @NotNull(message = "http 请求方法不能为空")
  35 + private String requestMethod;
  36 + /**
  37 + * 访问地址
  38 + */
  39 + @NotNull(message = "访问地址不能为空")
  40 + private String requestUrl;
  41 + /**
  42 + * 请求参数
  43 + */
  44 + @NotNull(message = "请求参数不能为空")
  45 + private String requestParams;
  46 + /**
  47 + * 用户 IP
  48 + */
  49 + @NotNull(message = "ip 不能为空")
  50 + private String userIp;
  51 + /**
  52 + * 浏览器 UA
  53 + */
  54 + @NotNull(message = "User-Agent 不能为空")
  55 + private String userAgent;
  56 +
  57 + /**
  58 + * 开始请求时间
  59 + */
  60 + @NotNull(message = "开始请求时间不能为空")
  61 + private Date startTime;
  62 + /**
  63 + * 结束请求时间
  64 + */
  65 + @NotNull(message = "结束请求时间不能为空")
  66 + private Date endTime;
  67 + /**
  68 + * 执行时长,单位:毫秒
  69 + */
  70 + @NotNull(message = "执行时长不能为空")
  71 + private Integer duration;
  72 + /**
  73 + * 结果码
  74 + */
  75 + @NotNull(message = "错误码不能为空")
  76 + private Integer resultCode;
  77 + /**
  78 + * 结果提示
  79 + */
  80 + private String resultMsg;
  81 +
  82 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/package-info.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.logger.apilog.core.service;
... ...
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
... ... @@ -37,6 +37,5 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
37 37 }
38 38 // 返回成功
39 39 ServletUtils.writeJSON(response, null);
40   -// ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.OK.value(), "退出成功")));
41 40 }
42 41 }
... ...
src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java
1 1 package cn.iocoder.dashboard.framework.web.config;
2 2  
  3 +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum;
3 4 import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter;
4 5 import cn.iocoder.dashboard.framework.web.core.filter.XssFilter;
5 6 import org.springframework.boot.context.properties.EnableConfigurationProperties;
  7 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
6 8 import org.springframework.context.annotation.Bean;
7 9 import org.springframework.context.annotation.Configuration;
8   -import org.springframework.core.annotation.Order;
9 10 import org.springframework.util.PathMatcher;
10 11 import org.springframework.web.bind.annotation.RestController;
11 12 import org.springframework.web.cors.CorsConfiguration;
... ... @@ -15,10 +16,8 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
15 16 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
16 17  
17 18 import javax.annotation.Resource;
  19 +import javax.servlet.Filter;
18 20  
19   -/**
20   - * Web 配置类
21   - */
22 21 @Configuration
23 22 @EnableConfigurationProperties({WebProperties.class, XssProperties.class})
24 23 public class WebConfiguration implements WebMvcConfigurer {
... ... @@ -39,8 +38,7 @@ public class WebConfiguration implements WebMvcConfigurer {
39 38 * 创建 CorsFilter Bean,解决跨域问题
40 39 */
41 40 @Bean
42   - @Order(Integer.MIN_VALUE)
43   - public CorsFilter corsFilter() {
  41 + public FilterRegistrationBean<CorsFilter> corsFilterBean() {
44 42 // 创建 CorsConfiguration 对象
45 43 CorsConfiguration config = new CorsConfiguration();
46 44 config.setAllowCredentials(true);
... ... @@ -50,25 +48,29 @@ public class WebConfiguration implements WebMvcConfigurer {
50 48 // 创建 UrlBasedCorsConfigurationSource 对象
51 49 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
52 50 source.registerCorsConfiguration("/**", config); // 对接口配置跨域设置
53   - return new CorsFilter(source);
  51 + return createFilterBean(new CorsFilter(source), FilterOrderEnum.CORS_FILTER);
54 52 }
55 53  
56 54 /**
57 55 * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容
58 56 */
59 57 @Bean
60   - @Order(Integer.MIN_VALUE)
61   - public RequestBodyCacheFilter requestBodyCacheFilter() {
62   - return new RequestBodyCacheFilter();
  58 + public FilterRegistrationBean<RequestBodyCacheFilter> requestBodyCacheFilter() {
  59 + return createFilterBean(new RequestBodyCacheFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
63 60 }
64 61  
65 62 /**
66 63 * 创建 XssFilter Bean,解决 Xss 安全问题
67 64 */
68 65 @Bean
69   - @Order(Integer.MIN_VALUE + 1000) // 需要保证在 RequestBodyCacheFilter 后面
70   - public XssFilter xssFilter(XssProperties properties, PathMatcher pathMatcher) {
71   - return new XssFilter(properties, pathMatcher);
  66 + public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
  67 + return createFilterBean(new XssFilter(properties, pathMatcher), FilterOrderEnum.XSS_FILTER);
  68 + }
  69 +
  70 + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
  71 + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
  72 + bean.setOrder(order);
  73 + return bean;
72 74 }
73 75  
74 76 }
... ...
src/main/java/cn/iocoder/dashboard/framework/web/core/enums/FilterOrderEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.web.core.enums;
  2 +
  3 +/**
  4 + * 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
  5 + *
  6 + * @author 芋道源码
  7 + */
  8 +public interface FilterOrderEnum {
  9 +
  10 + int CORS_FILTER = Integer.MIN_VALUE;
  11 +
  12 + int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
  13 +
  14 + // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
  15 +
  16 + int API_ACCESS_LOG_FILTER = -104; // 需要保证在 RequestBodyCacheFilter 后面
  17 +
  18 + int XSS_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
  19 +
  20 + // Spring Security Filter 默认为 -100,可见 SecurityProperties 配置属性类
  21 +
  22 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/RequestBodyCacheFilter.java
1 1 package cn.iocoder.dashboard.framework.web.core.filter;
2 2  
3   -import cn.hutool.core.util.StrUtil;
4   -import org.springframework.http.MediaType;
  3 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
5 4 import org.springframework.web.filter.OncePerRequestFilter;
6 5 import org.springframework.web.util.ContentCachingRequestWrapper;
7 6  
... ... @@ -29,7 +28,7 @@ public class RequestBodyCacheFilter extends OncePerRequestFilter {
29 28 @Override
30 29 protected boolean shouldNotFilter(HttpServletRequest request) {
31 30 // 只处理 json 请求内容
32   - return !StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
  31 + return !ServletUtils.isJsonRequest(request);
33 32 }
34 33  
35 34 }
... ...
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/XssRequestWrapper.java
... ... @@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
6 6 import cn.hutool.core.util.ReflectUtil;
7 7 import cn.hutool.core.util.StrUtil;
8 8 import cn.hutool.http.HTMLFilter;
  9 +import cn.iocoder.dashboard.util.servlet.ServletUtils;
9 10 import org.springframework.http.MediaType;
10 11  
11 12 import javax.servlet.ReadListener;
... ... @@ -56,7 +57,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
56 57 @Override
57 58 public ServletInputStream getInputStream() throws IOException {
58 59 // 如果非 json 请求,不进行 Xss 处理
59   - if (!StrUtil.startWithIgnoreCase(super.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
  60 + if (!ServletUtils.isJsonRequest(this)) {
60 61 return super.getInputStream();
61 62 }
62 63  
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobLogDO.java
... ... @@ -9,7 +9,7 @@ import lombok.*;
9 9 import java.util.Date;
10 10  
11 11 /**
12   - * 定时任务的执行日志 sys_job_log
  12 + * 定时任务的执行日志
13 13 *
14 14 * @author 芋道源码
15 15 */
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiAccessLogDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.logger;
  2 +
  3 +import cn.iocoder.dashboard.common.enums.UserTypeEnum;
  4 +import cn.iocoder.dashboard.common.pojo.CommonResult;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +import java.util.Date;
  11 +
  12 +/**
  13 + * API 访问日志
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@TableName("sys_api_access_log")
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class SysApiAccessLogDO extends BaseDO {
  25 +
  26 + /**
  27 + * 编号
  28 + */
  29 + @TableId
  30 + private Integer id;
  31 + /**
  32 + * 链路追踪编号
  33 + *
  34 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  35 + */
  36 + private String traceId;
  37 + /**
  38 + * 用户编号
  39 + */
  40 + private Integer userId;
  41 + /**
  42 + * 用户类型
  43 + *
  44 + * 枚举 {@link UserTypeEnum}
  45 + */
  46 + private Integer userType;
  47 + /**
  48 + * 应用名
  49 + *
  50 + * 目前读取 `spring.application.name` 配置项
  51 + */
  52 + private String applicationName;
  53 +
  54 + /**
  55 + * 请求方法名
  56 + */
  57 + private String requestMethod;
  58 + /**
  59 + * 访问地址
  60 + */
  61 + private String requestUrl;
  62 + /**
  63 + * 请求参数
  64 + *
  65 + * query: Query String
  66 + * body: Quest Body
  67 + */
  68 + private String requestParams;
  69 + /**
  70 + * 用户 IP
  71 + */
  72 + private String userIp;
  73 + /**
  74 + * 浏览器 UA
  75 + */
  76 + private String userAgent;
  77 +
  78 + /**
  79 + * 开始请求时间
  80 + */
  81 + private Date startTime;
  82 + /**
  83 + * 结束请求时间
  84 + */
  85 + private Date endTime;
  86 + /**
  87 + * 执行时长,单位:毫秒
  88 + */
  89 + private Integer duration;
  90 + /**
  91 + * 结果码
  92 + *
  93 + * 目前使用的 {@link CommonResult#getCode()} 属性
  94 + */
  95 + private Integer resultCode;
  96 + /**
  97 + * 结果提示
  98 + *
  99 + * 目前使用的 {@link CommonResult#getMsg()} 属性
  100 + */
  101 + private String resultMsg;
  102 +
  103 +}
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiErrorLogDO.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.dal.dataobject.logger;
  2 +
  3 +public class SysApiErrorLogDO {
  4 +}
... ...
src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java
1 1 package cn.iocoder.dashboard.util.servlet;
2 2  
3 3 import cn.hutool.core.io.IoUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 5 import cn.hutool.extra.servlet.ServletUtil;
5 6 import cn.iocoder.dashboard.util.json.JsonUtils;
6 7 import org.springframework.http.MediaType;
... ... @@ -8,6 +9,7 @@ import org.springframework.web.context.request.RequestAttributes;
8 9 import org.springframework.web.context.request.RequestContextHolder;
9 10 import org.springframework.web.context.request.ServletRequestAttributes;
10 11  
  12 +import javax.servlet.ServletRequest;
11 13 import javax.servlet.http.HttpServletRequest;
12 14 import javax.servlet.http.HttpServletResponse;
13 15 import java.io.IOException;
... ... @@ -86,4 +88,8 @@ public class ServletUtils {
86 88 return ServletUtil.getClientIP(request);
87 89 }
88 90  
  91 + public static boolean isJsonRequest(ServletRequest request) {
  92 + return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
  93 + }
  94 +
89 95 }
... ...