import com.alibaba.fastjson.JSONObject; import com.idoipo.infras.gateway.open.model.InvokeLogModel; import com.idoipo.infras.gateway.open.service.IInvokeLogService; import com.idoipo.infras.gateway.open.utils.MultiPartFormDateToJson; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; import org.springframework.util.StreamUtils; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.Date; import java.util.Map; /** * Create by liping on 2018/9/11 * 接口调用日志记录过滤器 */ @Component public class LogRecodePostFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(LogRecodePostFilter.class); @Autowired IInvokeLogService invokeLogService; @Override public String filterType() { return FilterConstants.POST_TYPE;//要打印返回信息,必须得用"post" } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER + 2; } @Override public boolean shouldFilter() { RequestContext context = RequestContext.getCurrentContext(); Boolean isSuccess = (boolean) context.get("isSuccess"); return isSuccess; } @Override public Object run() { try { logger.info("进入日志记录过滤器"); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); InputStream in = request.getInputStream(); String method = request.getMethod(); String interfaceMethod = request.getServletPath(); //logger.info("请求方法method={},url={}",method,interfaceMethod); String reqBody = StreamUtils.copyToString(in, Charset.forName("UTF-8")); int user = 0; String invokeUser = ""; if ("GET".equals(method.toUpperCase())) { Map<String, String[]> map = request.getParameterMap(); // 打印请求url参数 if (map != null) { StringBuilder sb = new StringBuilder(); sb.append("{"); for (Map.Entry<String, String[]> entry : map.entrySet()) { String key = entry.getKey(); String value = printArray(entry.getValue()); sb.append("[" + key + "=" + value + "]"); if ("user".equals(key)) { invokeUser = value; } else if ("userFlag".equals(key)) { user = Integer.parseInt(value); } } sb.append("}"); reqBody = sb.toString(); //logger.info("reqBody ={}" + reqBody); } } else if ("POST".equals(method.toUpperCase())) { //打印请求json参数 if (reqBody != null) { String conType = request.getHeader("content-type"); //post请求目前获取userFlag,user参数只支持multipart/form-data,application/json,对于其他方式不记录用户信息 if (conType.contains("multipart/form-data") || conType.contains("application/json")) { if (conType.contains("multipart/form-data")) { reqBody = MultiPartFormDateToJson.formDateToJson(reqBody); } //默认content-type传json-->application/json Object userObject; Object invokeUserObject; JSONObject jsonObject = JSONObject.parseObject(reqBody); userObject = jsonObject.get("userFlag"); if (null != userObject) { user = Integer.parseInt(userObject.toString()); } else { logger.warn("当前请求缺少userFlag"); } invokeUserObject = jsonObject.get("user"); if (null != userObject) { invokeUser = invokeUserObject.toString(); } else { logger.warn("当前请求缺少user"); } //logger.info("reqBody:={}" + reqBody); } } } // 打印response InputStream out = ctx.getResponseDataStream(); String outBody = StreamUtils.copyToString(out, Charset.forName("UTF-8")); boolean result = false; if (outBody != null && "" != outBody) { JSONObject jsonObject = JSONObject.parseObject(outBody); Object dataFlagObject = jsonObject.get("dataFlag"); if (null != dataFlagObject) { int flag = Integer.parseInt(dataFlagObject.toString()); if (flag == 1) { result = true; } } //logger.info("响应参数:={}" + outBody); } //必须重新写入流//重要!!! ctx.setResponseBody(outBody); InvokeLogModel logModel = new InvokeLogModel(); logModel.setUid(user); logModel.setInvokeUser(invokeUser); logModel.setInterfaceName(interfaceMethod); logModel.setInterfaceMethod(method); logModel.setInvokeStartTime(new Date()); logModel.setInvokeEndTime(null); logModel.setRequestParam(reqBody); logModel.setResponseResult(result); logModel.setResponseBody(outBody); invokeLogService.insertInvokerLog(logModel); } catch (IOException e) { logger.error("LogRecode IO异常", e); } return null; } String printArray(String[] arr) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { sb.append(arr[i]); if (i < arr.length - 1) { sb.append(","); } } return sb.toString(); } }
当是post请求的格式不是application/json格式,而是multipart/form-data格式时,数据不能直接转json对象,需要进行匹配,可以使用如下工具类
import com.alibaba.fastjson.JSON; import org.apache.commons.lang.StringUtils; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Optional; /** * 当数据 * Create by liping on 2018/9/14 */ public class MultiPartFormDateToJson { public static Map<String, String> toMap(String params) { Map<String, String> map = new HashMap<>(); //获得分隔符 String boundary = params.split("\r\n")[0]; //获得分割后的参数 String[] ps = Optional.ofNullable(params).orElse("").split(boundary); for (String p : ps) { if(p.equals("")) continue; if (p.equals("--\r\n")) continue; p = p.trim().replaceAll("\r\n", "&&"); String[] ds = p.split(";"); //获得参数名 String nameMeta = Arrays.asList(ds).stream() .filter(d -> d.trim().startsWith("name=")) .findAny() .orElse(""); String name = Optional.ofNullable(nameMeta.split("\"")[1]).orElse(""); //获得参数值 String value = Optional.ofNullable(StringUtils.substringAfter(p,"&&&&")).orElse(""); map.put(name, value); } return map; } public static String formDateToJson(String param){ return JSON.toJSONString(toMap(param)); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构