如何移除SoapCore XML响应中AuthRS标签的xmlns命名空间
如何移除SoapCore XML响应中AuthRS标签的xmlns命名空间
我之前在.NET 6 + SoapCore的项目里也碰到过一模一样的问题——明明用了MessageInspector处理响应,结果AuthRS标签上的xmlns还是去不掉。折腾了好一阵,总结出几个亲测有效的方案,你可以根据自己的场景选:
方案一:直接修改实体类和契约的命名空间配置
这是最省心的方法,如果那个xmlns是从你的实体类或者服务契约的注解里来的,直接把命名空间设为空就行:
修改AuthRS实体类
找到你的AuthRS类,把原来带命名空间的[XmlRoot]或者[DataContract]注解改成空命名空间:// 原来的代码可能是这样的: // [XmlRoot(Namespace = "https://app.testurl.com.br")] [XmlRoot(Namespace = "")] public class AuthRS { [XmlAttribute] public bool DebitCard { get; set; } [XmlAttribute] public string OrderNumber { get; set; } [XmlAttribute] public string ResponseCode { get; set; } [XmlAttribute] public string TransactionID { get; set; } [XmlAttribute] public string Version { get; set; } public string RedirectURL { get; set; } }检查服务契约的命名空间
如果你的[ServiceContract]注解里也指定了这个命名空间,同样改成空:// 原来的代码: // [ServiceContract(Namespace = "https://app.testurl.com.br")] [ServiceContract(Namespace = "")] public interface IAuthService { [OperationContract] AuthRS Authenticate(AuthRQ request); }
⚠️ 注意:这个方法会影响WSDL的生成,如果对接的客户端依赖原命名空间的WSDL,一定要先和对方确认再改。
方案二:修复MessageInspector的处理逻辑
你之前用MessageInspector没成功,大概率是因为没找到正确的AuthRS节点——因为节点带命名空间,直接用SelectSingleNode("//AuthRS")是找不到的。试试下面的修正版代码:
1. 编写自定义MessageInspector
using System.Xml; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; public class RemoveNamespaceInspector : IDispatchMessageInspector { // 接收请求时不需要处理,直接返回null public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { return null; } // 发送响应前修改XML public void BeforeSendReply(ref Message reply, object correlationState) { // 将Message转换为XmlDocument方便修改 var xmlDoc = new XmlDocument(); using (var reader = reply.GetReaderAtBodyContents()) { xmlDoc.Load(reader); } // 必须指定命名空间管理器才能找到带xmlns的AuthRS节点 var nsManager = new XmlNamespaceManager(xmlDoc.NameTable); nsManager.AddNamespace("targetNs", "https://app.testurl.com.br"); var authRsNode = xmlDoc.SelectSingleNode("//targetNs:AuthRS", nsManager); if (authRsNode != null) { // 移除xmlns属性 if (authRsNode.Attributes["xmlns"] != null) { authRsNode.Attributes.Remove(authRsNode.Attributes["xmlns"]); } // 把节点的命名空间设为空,避免序列化时重新加上 authRsNode.NamespaceURI = ""; } // 重新构建Message替换原响应 var newReply = Message.CreateMessage(reply.Version, reply.Headers.Action, new XmlNodeReader(xmlDoc.DocumentElement)); newReply.Headers.CopyHeadersFrom(reply.Headers); newReply.Properties.CopyProperties(reply.Properties); reply = newReply; } }
2. 注册MessageInspector到SoapCore
在Program.cs里把这个Inspector加到终结点配置中:
var builder = WebApplication.CreateBuilder(args); // 注册SoapCore和你的服务 builder.Services.AddSoapCore(); builder.Services.AddSingleton<IAuthService, AuthService>(); // 注册自定义Inspector builder.Services.AddSingleton<IDispatchMessageInspector, RemoveNamespaceInspector>(); var app = builder.Build(); app.UseEndpoints(endpoints => { endpoints.UseSoapEndpoint<IAuthService>("/AuthService.asmx", new SoapEncoderOptions { MessageEncoder = new TextMessageEncoder(MessageVersion.Soap11) }, endpointConfig => { // 把Inspector添加到终结点的调度器里 var inspector = app.Services.GetRequiredService<IDispatchMessageInspector>(); endpointConfig.DispatchMessageInspectors.Add(inspector); }); }); app.Run();
这个方案不会影响WSDL,适合不能修改契约命名空间的场景。
方案三:自定义Xml序列化器(适合结构固定的响应)
如果上面的方法都不行,还可以直接重写序列化逻辑,手动控制输出的XML结构:
using System.Xml; using System.Runtime.Serialization; public class AuthRsCustomSerializer : XmlObjectSerializer { private readonly DataContractSerializer _innerSerializer; public AuthRsCustomSerializer(Type type) { _innerSerializer = new DataContractSerializer(type, "", "", new DataContractSerializerSettings { Namespace = "" }); } public override bool IsStartObject(XmlDictionaryReader reader) => _innerSerializer.IsStartObject(reader); public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) => _innerSerializer.ReadObject(reader, verifyObjectName); public override void WriteEndObject(XmlDictionaryWriter writer) => _innerSerializer.WriteEndObject(writer); public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) => _innerSerializer.WriteObjectContent(writer, graph); // 手动写入AuthRS节点,不添加命名空间 public override void WriteStartObject(XmlDictionaryWriter writer, object graph) { var authRs = graph as AuthRS; if (authRs == null) { _innerSerializer.WriteStartObject(writer, graph); return; } // 直接写入不带命名空间的节点 writer.WriteStartElement("AuthRS"); // 手动写入所有属性 writer.WriteAttributeString("DebitCard", authRs.DebitCard.ToString().ToLower()); writer.WriteAttributeString("OrderNumber", authRs.OrderNumber); writer.WriteAttributeString("ResponseCode", authRs.ResponseCode); writer.WriteAttributeString("TransactionID", authRs.TransactionID); writer.WriteAttributeString("Version", authRs.Version); } }
然后在注册终结点时指定这个序列化器:
endpoints.UseSoapEndpoint<IAuthService>("/AuthService.asmx", new SoapEncoderOptions { MessageEncoder = new TextMessageEncoder(MessageVersion.Soap11) }, endpointConfig => { endpointConfig.Serializer = (type, settings) => new AuthRsCustomSerializer(type); });
这个方案需要手动处理每个属性,适合响应结构固定、不会经常变动的场景。
备注:内容来源于stack exchange,提问作者Jefferson Lopes
相关产品推荐
相关产品推荐

