.NET 7迁移WCF客户端时CreateMessage因转义ASCII字符失败
解决.NET 7 WCF客户端中0x07(响铃符)的字符检查异常问题
问题背景
将WCF客户端从.NET Framework迁移到.NET 7后,序列化包含ASCII 0x07分隔符的对象时,原本在.NET Framework中该字符会自动转义为,但在.NET 7中调用Message.ToString()时抛出System.ArgumentException: '\a', hexadecimal value 0x07, is an invalid character异常。直接使用XmlReader/XmlWriter时可通过CheckCharacters = false跳过检查,但WCF Message对象没有直接配置该属性的入口。
解决方案
方案1:自定义客户端消息格式化器(推荐)
通过实现IClientMessageFormatter扩展WCF的序列化逻辑,手动配置XmlWriter关闭字符检查:
public class CustomClientFormatter : IClientMessageFormatter { private readonly IClientMessageFormatter _originalFormatter; public CustomClientFormatter(IClientMessageFormatter originalFormatter) { _originalFormatter = originalFormatter; } public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) { var originalMessage = _originalFormatter.SerializeRequest(messageVersion, parameters); var bufferedCopy = originalMessage.CreateBufferedCopy(int.MaxValue); var clonedMessage = bufferedCopy.CreateMessage(); // 创建新消息,使用关闭字符检查的XmlWriter写入内容 var newMessage = Message.CreateMessage(messageVersion, clonedMessage.Headers.Action); var writerSettings = new XmlWriterSettings { CheckCharacters = false }; using (var writer = XmlWriter.Create(newMessage.BodyWriter.CreateBufferedCopy(int.MaxValue).CreateReader().ReadSubtree(), writerSettings)) { clonedMessage.WriteBodyContents(writer); } // 复制原消息的头部和属性 foreach (var header in clonedMessage.Headers) { newMessage.Headers.Add(header); } foreach (var prop in clonedMessage.Properties) { newMessage.Properties[prop.Key] = prop.Value; } return newMessage; } public object[] DeserializeReply(Message message, object[] parameters) { return _originalFormatter.DeserializeReply(message, parameters); } }
再通过端点行为注入自定义格式化器:
public class CustomFormatterBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { foreach (var operation in clientRuntime.ClientOperations) { operation.Formatter = new CustomClientFormatter(operation.Formatter); } } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
最后在客户端实例中添加该行为:
var client = new MyServiceClient(); client.Endpoint.Behaviors.Add(new CustomFormatterBehavior());
方案2:预转义0x07字符
在构造Request对象时,手动将0x07替换为XML实体,从源头上避免触发字符检查:
// 假设Request类的SerializedData字段包含分隔符 request.SerializedData = request.SerializedData.Replace("\a", ""); Message wcfMessage = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "MyServiceAction", request); var wcfMessageXml = wcfMessage.ToString();
注意:需确认服务端能正确解析预转义的实体,避免兼容性问题。
方案3:反射修改内部XmlWriterSettings(临时 workaround)
通过反射修改WCF Message内部使用的XmlWriterSettings,强制关闭字符检查:
var request = new Request(...); Message wcfMessage = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "MyServiceAction", request); // 反射获取并修改内部设置 var bodyWriterProp = typeof(Message).GetProperty("BodyWriter", BindingFlags.Instance | BindingFlags.NonPublic); var bodyWriter = bodyWriterProp?.GetValue(wcfMessage); var settingsField = bodyWriter?.GetType().GetField("_xmlWriterSettings", BindingFlags.Instance | BindingFlags.NonPublic); if (settingsField != null) { var settings = (XmlWriterSettings)settingsField.GetValue(bodyWriter); settings.CheckCharacters = false; } var wcfMessageXml = wcfMessage.ToString();
注意:此方法依赖.NET WCF的内部实现,后续版本可能失效,仅作为临时应急方案。
总结
优先选择方案1,这是符合WCF扩展规范的方式,兼容性和可维护性最好;若对象结构简单,方案2是最轻便的选择;方案3仅建议在紧急场景下临时使用。
内容的提问来源于stack exchange,提问作者Andy W
相关产品推荐
相关产品推荐

