SAP CPI中Multipart/mixed消息Groovy脚本处理需求咨询
我来给你详细说说在SAP CPI里处理这个嵌套Multipart/mixed payload的具体方案,分脚本实现思路和CPI操作配置两步来讲:
Groovy脚本实现思路
你的payload是两层嵌套的Multipart结构(外层batch,内层changeset),核心思路是逐层解析、修改JSON内容再重新组装,具体步骤如下:
1. 解析嵌套的Multipart结构
先用Apache的MultipartStream工具逐层拆解payload:先处理外层的--batch边界,遍历每个子part;遇到内层changeset的Multipart部分时,再拆解内层的每个HTTP请求part。
2. 提取HTTP请求中的JSON Body
每个changeset里的内容是application/http类型的PATCH请求,我们通过分割HTTP头和Body(找两个连续的换行符),把JSON部分单独提取出来,转成Groovy的Map对象方便修改。
3. 字段替换与移除
遍历每个JSON Map:如果同时存在PurReqnExternalSystemType和PurchasingDocumentCategory字段,就把前者的值替换为后者,然后删除PurchasingDocumentCategory字段。
4. 重新组装Multipart内容
把修改后的JSON替换回原来的HTTP请求部分,再按照原始的Multipart格式,用正确的边界符重新拼接所有part,确保输出的payload格式符合要求。
完整Groovy脚本示例
import org.apache.commons.fileupload.MultipartStream import groovy.json.JsonSlurper import groovy.json.JsonBuilder def processMultipartPayload(String payload, String outerBoundary, String innerBoundary) { def result = new StringBuilder() def inputStream = new ByteArrayInputStream(payload.getBytes()) // 处理外层batch multipart def outerMultipart = new MultipartStream(inputStream, outerBoundary.getBytes(), 8192, null) boolean hasNextOuter = outerMultipart.skipPreamble() while (hasNextOuter) { def headers = outerMultipart.readHeaders() def partContent = new ByteArrayOutputStream() outerMultipart.readBodyData(partContent) def partStr = partContent.toString() // 判断是否是内层changeset的multipart if (headers.toLowerCase().contains('content-type: multipart/mixed; boundary=' + innerBoundary)) { def innerResult = new StringBuilder() def innerInputStream = new ByteArrayInputStream(partStr.getBytes()) def innerMultipart = new MultipartStream(innerInputStream, innerBoundary.getBytes(), 8192, null) boolean hasNextInner = innerMultipart.skipPreamble() while (hasNextInner) { def innerHeaders = innerMultipart.readHeaders() def innerPartContent = new ByteArrayOutputStream() innerMultipart.readBodyData(innerPartContent) def innerPartStr = innerPartContent.toString() // 处理application/http类型的part(包含PATCH请求) if (innerHeaders.toLowerCase().contains('content-type: application/http')) { // 分割HTTP头和JSON body(两个连续换行) def parts = innerPartStr.split('\r\n\r\n', 2) if (parts.length == 2) { def httpHeader = parts[0] def jsonStr = parts[1] def jsonSlurper = new JsonSlurper() def jsonMap = jsonSlurper.parseText(jsonStr) // 替换字段并移除目标字段 if (jsonMap.PurReqnExternalSystemType != null && jsonMap.PurchasingDocumentCategory != null) { jsonMap.PurReqnExternalSystemType = jsonMap.PurchasingDocumentCategory jsonMap.remove('PurchasingDocumentCategory') } // 重新生成JSON字符串 def newJsonStr = new JsonBuilder(jsonMap).toPrettyString() // 拼接回HTTP请求内容 innerPartStr = "${httpHeader}\r\n\r\n${newJsonStr}" } } // 添加到内层结果集 innerResult.append("--${innerBoundary}\r\n") innerResult.append(innerHeaders) innerResult.append("\r\n") innerResult.append(innerPartStr) innerResult.append("\r\n") hasNextInner = innerMultipart.readBoundary() } // 添加内层Multipart结束符 innerResult.append("--${innerBoundary}--\r\n") partStr = innerResult.toString() } // 添加到外层结果集 result.append("--${outerBoundary}\r\n") result.append(headers) result.append("\r\n") result.append(partStr) result.append("\r\n") hasNextOuter = outerMultipart.readBoundary() } // 添加外层Multipart结束符 result.append("--${outerBoundary}--") return result.toString() } // 获取原始payload和边界符(推荐从Content-Type头解析,这里简化演示) def payload = message.getBody(String) def contentType = message.getHeaders().get('Content-Type', '') def outerBoundary = contentType =~ /boundary=(.*)/ ? (contentType =~ /boundary=(.*)/)[0][1] : 'batch' def innerBoundary = 'changeset' // 也可以从内层Content-Type头解析,这里硬编码简化 // 处理payload并设置回消息体 def processedPayload = processMultipartPayload(payload, outerBoundary, innerBoundary) message.setBody(processedPayload)
SAP CPI操作建议
1. 脚本放置位置
在你的Integration Flow中,把这个Groovy Script步骤放在Receive Step之后、任何其他映射/转换步骤之前,确保原始payload未被修改时就进行处理。
2. 边界符解析优化
脚本里已经包含了从Content-Type头解析外层边界符的逻辑,不要硬编码边界符(除非你能100%确定边界符不会变),这样能适配不同场景的Multipart请求。
3. 测试验证
- 先在本地用Groovy Console测试脚本:抓取CPI里的原始payload,复制到本地编辑器运行脚本,检查修改后的JSON是否符合要求。
- 部署到CPI后,用Message Monitor查看处理前后的payload对比,确保格式和字段修改都正确。
4. 头信息维护
处理完payload后,确保Content-Type头保持不变(依然是multipart/mixed; boundary=batch),避免下游系统解析出错。如果头信息被修改,可以在脚本末尾添加:
message.setHeader('Content-Type', "multipart/mixed; boundary=${outerBoundary}")
内容的提问来源于stack exchange,提问作者user928700

