This commit is contained in:
oneao committed 2026-03-25 21:27:48 +08:00
1 parent a4e59dd5d1
commit c7240ea7c9
16 files changed
+598 -476

No files matched your search

@@ -2,7 +2,9 @@ package cn.g3soft.config;
import cn.g3soft.interceptor.AuthInterceptor;
import cn.g3soft.interceptor.DataSourceInterceptor;
import cn.g3soft.interceptor.RequestBodyCacheInterceptor;
import cn.g3soft.interceptor.RequestWrapperFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -11,9 +13,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestBodyCacheInterceptor())
.addPathPatterns("/**");
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(
@@ -28,4 +27,13 @@ public class WebMvcConfig implements WebMvcConfigurer {
"/error"
);
}
@Bean
public FilterRegistrationBean<RequestWrapperFilter> requestWrapperFilter() {
FilterRegistrationBean<RequestWrapperFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new RequestWrapperFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1); // 一定要靠前
return registration;
}
}
@@ -1,6 +1,5 @@
package cn.g3soft.exception;
import cn.g3soft.interceptor.RequestBodyCacheInterceptor;
import cn.g3soft.utils.ExceptionUtils;
import cn.g3soft.utils.Result;
import lombok.extern.slf4j.Slf4j;
@@ -14,9 +13,7 @@ import javax.servlet.http.HttpServletRequest;
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<ExceptionDetailInfo> handleException(Exception ex, HttpServletRequest request) {
String requestBody = RequestBodyCacheInterceptor.getRequestBodyFromThreadLocal();
ExceptionDetailInfo info = ExceptionUtils.extractExceptionInfo(ex, requestBody, "cn.g3soft");
ExceptionDetailInfo info = ExceptionUtils.extractExceptionInfo(ex, request, "cn.g3soft");
log.error("异常发生在 {}#{}({}:{}),ID: {},信息: {}",
info.getClassName(),
@@ -1,60 +0,0 @@
package cn.g3soft.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
@Component
public class RequestBodyCacheInterceptor implements HandlerInterceptor {
private static final ThreadLocal<String> requestBodyThreadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 仅在异常发生时读取请求体
if (response.isCommitted()) {
return true;
}
// 缓存请求体到 ThreadLocal
if (isExceptionRequest(request)) {
String requestBody = getRequestBody(request);
requestBodyThreadLocal.set(requestBody); // 将请求体保存到 ThreadLocal
}
return true;
}
private boolean isExceptionRequest(HttpServletRequest request) {
// 你可以根据某些条件判断是否需要读取请求体,比如请求头或者请求的 URI
// 这里简单示范,假设在异常时才读取
return true; // 仅在异常发生时才读取请求体
}
private String getRequestBody(HttpServletRequest request) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
StringBuilder bodyBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
bodyBuilder.append(line);
}
return bodyBuilder.toString();
}
// 提供获取请求体的方法
public static String getRequestBodyFromThreadLocal() {
return requestBodyThreadLocal.get();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除ThreadLocal中的请求体
requestBodyThreadLocal.remove();
}
}
@@ -0,0 +1,39 @@
package cn.g3soft.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class RequestWrapperFilter implements Filter {
private static final int MAX_CACHE_SIZE = 1024 * 50; // 50KB
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String contentType = req.getContentType();
// 只处理 JSON,排除文件上传
if (contentType != null
&& contentType.contains("application/json")
&& !contentType.contains("multipart/form-data")) {
ContentCachingRequestWrapper wrapper =
new ContentCachingRequestWrapper(req, MAX_CACHE_SIZE);
chain.doFilter(wrapper, response);
} else {
chain.doFilter(request, response);
}
}
}
@@ -3,6 +3,7 @@ package cn.g3soft.utils;
import cn.g3soft.exception.ExceptionDetailInfo;
import cn.g3soft.utils.uniqueId.idgen.UniqueId;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
@@ -10,10 +11,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class ExceptionUtils {
@@ -43,7 +41,6 @@ public class ExceptionUtils {
}
}
Map<String, String[]> parameterMap = request.getParameterMap();
String briefMessage;
if (ex.getMessage() != null) {
@@ -67,54 +64,38 @@ public class ExceptionUtils {
briefMessage = ex.getClass().getSimpleName();
}
String paramJson = null;
// 先尝试从parameterMap获取普通参数
Map<String, String> paramMap = new HashMap<>();
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
paramMap.put(paramName, request.getParameter(paramName));
}
// ================= 参数获取 =================
String paramJson = "{}";
// 如果普通参数为空,尝试读取请求体(用于@RequestBody)
if (paramMap.isEmpty()) {
try {
// 获取请求体内容
ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8));
StringBuilder bodyBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
bodyBuilder.append(line);
try {
// 1️⃣ URL参数
Map<String, String[]> parameterMap = request.getParameterMap();
if (!parameterMap.isEmpty()) {
Map<String, String> simpleMap = new HashMap<>();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
simpleMap.put(entry.getKey(), Arrays.toString(entry.getValue()));
}
String requestBody = bodyBuilder.toString();
paramJson = JsonUtils.toJson(simpleMap);
} else {
// 2️⃣ Body(重点)
if (request instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper =
(ContentCachingRequestWrapper) request;
if (!requestBody.isEmpty()) {
// 如果是JSON格式,可以尝试解析
try {
// 尝试将JSON字符串格式化为可读格式
ObjectMapper mapper = new ObjectMapper();
Object jsonObject = mapper.readValue(requestBody, Object.class);
paramJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
} catch (Exception e) {
// 如果不是JSON,直接使用原始字符串
paramJson = requestBody.length() > 1000 ?
requestBody.substring(0, 1000) + "..." : requestBody;
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
String body = new String(buf, StandardCharsets.UTF_8);
// 截断,防止过长
paramJson = body.length() > 1000
? body.substring(0, 1000) + "..."
: body;
}
} else {
paramJson = "{}";
}
} catch (IOException e) {
paramJson = "请求体读取失败: " + e.getMessage();
}
} else {
// 如果有普通参数,则序列化普通参数
try {
paramJson = JsonUtils.toJson(paramMap);
} catch (Exception e) {
paramJson = "参数序列化失败: " + e.getMessage();
}
} catch (Exception e) {
paramJson = "参数解析失败: " + e.getMessage();
}
return ExceptionDetailInfo.builder()