背景 使用Spring Cloud Gateway作為網關時有時候一個請求是既包含excel又包含json的表單數據,出於各種層面考慮網關需要獲取並更新其中的json數據 依賴 Spring Boot版本:2.7.15 Hutool: 5.8.21 Java: 11 實現邏輯 實現分為2個部分 使用 ...
背景
使用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
@Slf4j
public class RequestModifyFilter implements GlobalFilter, Ordered {
@Autowired
private ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter;
@Autowired
private JsonRequestBodyRewriteService jsonRequestBodyRewriteService;
@Autowired
private FormDataRequestBodyRewriteService formDataRequestBodyRewriteService;
@Override
public Mono<Void> 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
@Slf4j
public class FormDataRequestBodyRewriteService implements RewriteFunction<byte[], byte[]> {
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<byte[]> 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