背景
使用Spring Cloud Gateway作为网关时有时候一个请求是既包含excel又包含json的表单数据,出于各种层面考虑网关需要获取并更新其中的json数据
依赖
- Spring Boot版本:2.7.15
- Hutool: 5.8.21
- Java: 11
实现逻辑
实现分为2个部分
- 使用上文提到的ModifyRequestBodyGatewayFilterFactory类来修改请求体,这样最后就不用我们手动包装
- 核心service通过将表单转为String,然后根据其中的boundary进行分割,提取修改json报文部分后再进行组装
注意:示例代码的核心service处理的表单内容只是2个,Json数据的key指定为json,另一个excel文件流
自定义filter
@Component@Slf4jpublic class RequestModifyFilter implements GlobalFilter, Ordered { @Autowired private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; @Autowired private JsonRequestBodyRewriteService jsonRequestBodyRewriteService; @Autowired private FormDataRequestBodyRewriteService formDataRequestBodyRewriteService; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { MediaType mediaType = exchange.getRequest().getHeaders().getContentType(); if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) { // 纯json报文处理逻辑 return modifyRequestBodyFilter .apply( new ModifyRequestBodyGatewayFilterFactory.Config() .setRewriteFunction(byte[].class, byte[].class, jsonRequestBodyRewriteService)) .filter(exchange, chain); } else if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType)) { // form表单数据处理 return modifyRequestBodyFilter .apply( new ModifyRequestBodyGatewayFilterFactory.Config() .setRewriteFunction(byte[].class, byte[].class, formDataRequestBodyRewriteService)) .filter(exchange, chain); } else { return filter(exchange, chain); } } @Override public int getOrder() { return OrderConstant.REQUEST_MODIFY_FILTER.getOrder(); }}
核心service
@Service@Slf4jpublic class FormDataRequestBodyRewriteService implements RewriteFunction { private final String BOUNDARY_PREFIX_IN_CONTENT_TYPE = "----WebKitFormBoundary"; private final String BOUNDARY_PREFIX_IN_FORM_DATA = "------WebKitFormBoundary"; private final String BOUNDARY_SUFFIX = "--\r\n"; @Override public Publisher apply(ServerWebExchange exchange, byte[] body) { String finalResultString = ""; // 将表单转为字符串格式从而根据boundary分割表单数据。注意这里不能用默认编码 String request = StrUtil.str(body, StandardCharsets.ISO_8859_1); // 获取boundary的随机字符信息 String contentType = exchange.getRequest().getHeaders().getContentType().toString(); String randomStr = contentType.substring(contentType.indexOf(BOUNDARY_PREFIX_IN_CONTENT_TYPE) + BOUNDARY_PREFIX_IN_CONTENT_TYPE.length()); // 这里和前端约定json数据的表单key为json String keyPart = "^\r\nContent-Disposition: form-data; name=\"json\""; Pattern r = Pattern.compile(keyPart); // 根据表单内分割线进行分割。并通过关键段落keyPart来找到目标json数据 String[] split = request.split(BOUNDARY_PREFIX_IN_FORM_DATA + randomStr); for (int x = 0; x < split.length - 1; x++) { Matcher m = r.matcher(split[x]); if (m.find()) { // 找到了json报文部分数据 String originalJsonString = split[x]; // 找到 JSON 数据的起始和结束位置 int startIndex = originalJsonString.indexOf("{\""); int endIndex = originalJsonString.indexOf("\"}") + 2; // 提取 JSON 数据 String jsonData = originalJsonString.substring(startIndex, endIndex); log.info("原始报文为:{}", jsonData); JSONObject jsonObject = JSONUtil.parseObj(jsonData); jsonObject.set("empId", "2345"); jsonObject.set("department", "Engineering"); String modifiedString = originalJsonString.substring(0, startIndex) + jsonObject + originalJsonString.substring(endIndex); log.info("修改后报文为:{}", modifiedString); // 重新组装split数组 finalResultString = finalResultString + modifiedString + BOUNDARY_PREFIX_IN_FORM_DATA + randomStr; } else { // 重组表单数据 finalResultString = finalResultString + split[x] + BOUNDARY_PREFIX_IN_FORM_DATA + randomStr; } } // 补上最后一截数据 finalResultString = finalResultString + BOUNDARY_SUFFIX; return Mono.just(finalResultString.getBytes(StandardCharsets.ISO_8859_1)); }}
相关代码
https://github.com/eastcukt/demo-gatway
其他
核心service获取表单中的json数据逻辑挺复杂,根本原因是没有合适的方法进行对象转换,如果有像使用@RequestPart(value = “json”)注解一样方便的方法将会非常方便也不用自己截取,各位大佬有更方便的方法感谢分享一下
参考
https://blog.csdn.net/qq_36966137/article/details/128536391