RAD Studio 11编译Android跨平台SOAP客户端时,如何禁用请求中的BOM(基于WSDL导入器生成代码)
我之前处理过类似的跨平台SOAP客户端问题,结合RAD Studio 11中Delphi SOAP组件的特性,针对你遇到的Android平台请求带UTF-8 BOM导致服务器解析失败的问题,给你几个可行的解决方案:
问题根源
Win32/Win64平台下THTTPRIO生成的SOAP请求默认不会添加UTF-8 BOM,但Android平台的XML流处理实现有差异,会自动在UTF-8编码的XML开头插入BOM(0xEF,0xBB,0xBF),而你的OpenEdge WEB服务不支持带BOM的XML解析,因此触发了Content is not allowed in prolog的SAX解析错误。
解决方案
方法1:通过OnBeforeExecute事件移除BOM
最简单的方式是在SOAP请求发送前,拦截并修改请求流,去掉开头的BOM字节。你可以给THTTPRIO绑定OnBeforeExecute事件,然后在事件处理中清理流:
首先添加一个移除BOM的工具函数:
procedure RemoveUTF8BOM(AStream: TStream); var BOMBytes: array[0..2] of Byte; ReadBytes: Integer; begin AStream.Position := 0; ReadBytes := AStream.Read(BOMBytes, SizeOf(BOMBytes)); // 检查开头是否是UTF-8 BOM if (ReadBytes = 3) and (BOMBytes[0] = $EF) and (BOMBytes[1] = $BB) and (BOMBytes[2] = $BF) then begin // 移除BOM:将流中剩余内容前移覆盖BOM位置 AStream.Size := AStream.Size - 3; AStream.Position := 3; AStream.CopyFrom(AStream, AStream.Size - 3); AStream.Position := 0; end else AStream.Position := 0; end;
然后在你的GetLBLObj函数中,给RIO绑定事件处理:
// 在创建RIO后添加事件绑定 RIO.OnBeforeExecute := procedure(const MethodName: string; SOAPRequest: TStream) begin RemoveUTF8BOM(SOAPRequest); end;
方法2:修改SOAPXMLDoc的编码设置
THTTPRIO内部使用SOAPXMLDoc生成XML内容,我们可以直接修改这个组件的属性,强制它生成不带BOM的UTF-8 XML:
在GetLBLObj函数中,添加以下代码(放在RIO创建后):
if Assigned(RIO.SOAPXMLDoc) then begin // 设置XML编码为UTF-8 RIO.SOAPXMLDoc.Encoding := 'UTF-8'; // 禁用BOM写入(仅当SOAPXMLDoc是TXMLDocument实例时生效) if RIO.SOAPXMLDoc is TXMLDocument then TXMLDocument(RIO.SOAPXMLDoc).WriteBOM := False; // 确保内容类型头正确 RIO.HTTPWebNode.ContentType := 'text/xml; charset=utf-8'; end;
方法3:替换THTTPRIO的输出流类型
如果上面的方法都不生效,你可以自定义TStringStream来控制BOM的写入,替换RIO默认的流:
在GetLBLObj函数中,修改RIO的OnBeforeExecute事件,将原流替换为不带BOM的UTF-8流:
RIO.OnBeforeExecute := procedure(const MethodName: string; SOAPRequest: TStream) var TempStream: TStringStream; begin if SOAPRequest is TStringStream then begin // 用不带BOM的UTF-8编码重新创建流 TempStream := TStringStream.Create(TStringStream(SOAPRequest).DataString, TEncoding.UTF8); try TempStream.WriteBOM := False; // 覆盖原流内容 SOAPRequest.Position := 0; SOAPRequest.Size := 0; TempStream.Position := 0; SOAPRequest.CopyFrom(TempStream, TempStream.Size); finally TempStream.Free; end; end; end;
验证建议
修改完成后,再次用TCPMon拦截Android应用的SOAP请求,确认请求开头不再有3字节的BOM,直接以<?xml version="1.0"?>开头,此时OpenEdge服务器应该就能正常解析请求了。
内容的提问来源于stack exchange,提问作者Dencho Karaivanov




