You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET ASMX WebService命名空间适配及SOAP转REST问题

ASMX WebService适配第三方SOAP消息问题排查与解决

需求与背景

需要搭建中间服务,接收第三方SOAP消息并转发至REST微服务。因消息发送端无法修改,选择ASP.NET ASMX WebService处理SOAP请求,但开发中遇到兼容性问题。

第三方发送的SOAP消息结构

<s:Envelope xmlns:a="http://<sender-website-containing-this-schema>/soap/encoding"
    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="<SOME_STRING_WITH_THIS_FORMAT>">
    <s:Body>
        <ns1:EndpointRelatedFunction>
           <data></data>
           <!-- 其他字段 -->
        </ns1:EndpointRelatedFunction>
    </s:Body>
</s:Envelope>

自研ASMX服务代码

namespace ASMXWebService
{
    /// <summary>
    /// Summary description for ProxySOAP
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // 若允许通过ASP.NET AJAX调用,取消下方注释
    // [System.Web.Script.Services.ScriptService]
    public class ASMXWebService : WebService
    {
        [WebMethod]
        public string EndpointRelatedFunction(object data)
        {
            return "Hello World";
        }
    }
}

ASMX服务兼容的SOAP消息示例

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <EndpointRelatedFunction xmlns="http://tempuri.org/">
      <data />
    </EndpointRelatedFunction>
  </soap:Body>
</soap:Envelope>

报错信息

用Postman发送第三方SOAP消息时返回500错误:

soap:VersionMismatchSystem.Web.Services.Protocols.SoapException: Possible SOAP version mismatch:
Envelope namespace <http://<sender-website-containing-this-schema>/soap/encoding> was 
unexpected. Expecting http://schemas.xmlsoap.org/soap/envelope/. at 
System.Web.Services.Protocols.SoapServerProtocol.CheckHelperVersion() at 
System.Web.Services.Protocols.SoapServerProtocol.Initialize() at 
System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, 
HttpRequest request, HttpResponse response, Boolean& abortProcessing)

疑问点

  • 如何在ASMX代码中定义自定义命名空间前缀(如:a或:ns1)?
  • xmlns:ns1的非URI格式值是什么含义?

后续更新

更新1

当命名空间URI可正常访问时,服务无需修改即可解析XML,此前错误是因网络无法访问该URI导致。当前核心问题变为ns1对应的非URI格式命名空间,已联系发送端维护人员协调。

更新2

发送端将ns1对应的值改为服务原命名空间http://tempuri.org/后仍报错,服务期望消息格式为<EndpointRelatedFunction xmlns="WEB_SERVICE_NAMESPACE_THAT_PREVIOUSLY_WAS_TEMPURI">,而非带前缀的<ns1:EndpointRelatedFunction>。目前尝试通过SoapExtension在反序列化前修改XML以适配服务Schema。

解决方案建议

1. SoapExtension实现XML预处理

通过自定义SoapExtension拦截SOAP请求,修改XML结构,将带命名空间前缀的节点转换为服务期望的默认命名空间格式:

public class SoapNamespaceRewriteExtension : SoapExtension
{
    private Stream oldStream;
    private Stream newStream;

    public override object GetInitializer(Type serviceType) => null;
    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) => null;
    public override void Initialize(object initializer) { }

    public override Stream ChainStream(Stream stream)
    {
        oldStream = stream;
        newStream = new MemoryStream();
        return newStream;
    }

    public override void ProcessMessage(SoapMessage message)
    {
        switch (message.Stage)
        {
            case SoapMessageStage.BeforeDeserialize:
                RewriteNamespace(oldStream, newStream);
                newStream.Position = 0;
                break;
            case SoapMessageStage.AfterSerialize:
                CopyStream(newStream, oldStream);
                break;
        }
    }

    private void RewriteNamespace(Stream input, Stream output)
    {
        using (StreamReader reader = new StreamReader(input))
        using (StreamWriter writer = new StreamWriter(output))
        {
            string xml = reader.ReadToEnd();
            // 替换带前缀节点为服务期望的默认命名空间格式
            xml = xml.Replace("<ns1:EndpointRelatedFunction>", "<EndpointRelatedFunction xmlns=\"http://tempuri.org/\">");
            xml = xml.Replace("</ns1:EndpointRelatedFunction>", "</EndpointRelatedFunction>");
            writer.Write(xml);
        }
    }

    private void CopyStream(Stream from, Stream to)
    {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = from.Read(buffer, 0, buffer.Length)) > 0)
        {
            to.Write(buffer, 0, bytesRead);
        }
    }
}

在ASMX服务类上添加特性启用该扩展:

[SoapExtensionAttribute(typeof(SoapNamespaceRewriteExtension), Priority = 1)]
public class ASMXWebService : WebService
{
    // 原有代码
}

2. 命名空间前缀说明

ASMX服务不直接控制命名空间前缀定义,前缀是XML解析层面的概念,只要节点绑定的命名空间URI与服务定义一致,前缀不影响解析。但第三方消息的命名空间URI必须合法,若涉及Schema验证则需确保URI可访问或服务能离线解析。

3. 是否需要切换到WCF?

若后续遇到更多SOAP兼容性问题(如复杂类型解析、非标准SOAP协议),WCF是更优选择:

  • WCF对SOAP协议支持更灵活,可自定义绑定、消息编码器,适配非标准格式;
  • 内置强大的消息拦截与转换能力,无需手动编写SoapExtension;
  • ASMX已被微软标记为过时技术,WCF的维护性和扩展性更优。

WCF实现核心示例:
通过IDispatchMessageInspector拦截并修改请求消息:

public class SoapNamespaceInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        XmlDocument doc = new XmlDocument();
        using (MemoryStream ms = new MemoryStream())
        {
            request.WriteMessage(ms);
            ms.Position = 0;
            doc.Load(ms);
            XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
            nsManager.AddNamespace("ns1", "http://tempuri.org/");
            XmlNodeList nodes = doc.SelectNodes("//ns1:EndpointRelatedFunction", nsManager);
            
            foreach (XmlNode node in nodes)
            {
                node.Attributes.Append(doc.CreateAttribute("xmlns", "", "http://tempuri.org/"));
                node.Name = "EndpointRelatedFunction";
            }
            
            ms.SetLength(0);
            doc.Save(ms);
            ms.Position = 0;
            XmlReader reader = XmlReader.Create(ms);
            request = Message.CreateMessage(reader, int.MaxValue, request.Version);
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState) { }
}

// 注册Inspector到服务行为
public class CustomServiceBehavior : IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                ed.DispatchRuntime.MessageInspectors.Add(new SoapNamespaceInspector());
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}

内容的提问来源于stack exchange,提问作者hello-there

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:37:15