如何更改 ASMX Web 服务的 WSDL 端口地址?

问题描述 投票:0回答:1

我们有一些旧的 ASMX Web 服务,它们已移至额外的安全层后面。 WSDL 是自动生成的运行时,现在使用其运行的服务器的 IP,而不是公共 DNS。如何通过代码或 web.config 更改公共 DNS 的 IP?

Web 服务通过带有查询字符串的 HTTP POST/GET 提供 SOAP,以及更常见的场景,通过带有 SOAP-request-body 的 HTTP POST 提供 SOAP。我想将本地 IP 与运行时生成的 WSDL 的公共 DNS 名称进行切换。我已经使用 SoapExtensionReflector 在常见场景中成功做到了这一点,但它不适用于使用查询字符串的 WSDL 端口。

SoapExtensionReflector 的简化版本如下所示:

public class MySoapExtensionReflector : SoapExtensionReflector
{
    public override void ReflectDescription()
    {
        var description = ReflectionContext.ServiceDescription;

        foreach (Service service in description.Services)
        {
            foreach (Port port in service.Ports)
            {
                foreach (ServiceDescriptionFormatExtension extension in port.Extensions)
                {
                    SoapAddressBinding binding = extension as SoapAddressBinding;
                    if (null != binding)
                    {
                        binding.Location = Regex.Replace(binding.Location,
                                           "http://localhost:2017", 
                                           "https://example.com");
                    }
                }
            }
        }
    }

    public override void ReflectMethod()
    {
        // No implementation
    }
}

web.config的相关部分:

<system.web>
    <webServices>
      <soapExtensionReflectorTypes>
        <add type="MyService.MySoapExtensionReflector, MyService"/>
      </soapExtensionReflectorTypes>
      <protocols>
        <add name="HttpGet"/>
        <add name="HttpPost"/>
      </protocols>
    </webServices>
</system.web>

这会在 WSDL 中产生以下结果:

<wsdl:service name="MyService">
    <wsdl:port name="MyServiceSoap" binding="tns:MyServiceSoap">
        <soap:address location="https://example.com/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceSoap12" binding="tns:MyServiceSoap12">
        <soap12:address location="https://example.com/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceHttpGet" binding="tns:MyServiceHttpGet">
        <http:address location="http://localhost:2017/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceHttpPost" binding="tns:MyServiceHttpPost">
        <http:address location="http://localhost:2017/Myservice.asmx"/>
    </wsdl:port>
</wsdl:service>

这并不是理想的结果。最后两个端口由协议生成 - web.config 中的 HttpPost 和 HttpGet 定义。

如何更改此设置,以便 WSDL 端口具有所需的主机名?

结果应该是这样的:

<wsdl:service name="MyService">
    <wsdl:port name="MyServiceSoap" binding="tns:MyServiceSoap">
        <soap:address location="https://example.com/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceSoap12" binding="tns:MyServiceSoap12">
        <soap12:address location="https://example.com/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceHttpGet" binding="tns:MyServiceHttpGet">
        <http:address location="http://example.com/Myservice.asmx"/>
    </wsdl:port>
    <wsdl:port name="MyServiceHttpPost" binding="tns:MyServiceHttpPost">
        <http:address location="http://example.com/Myservice.asmx"/>
    </wsdl:port>
</wsdl:service>
c# .net asmx
1个回答
0
投票

解决方案很少,我会按照从简单到准确的顺序进行排序。 :)

  1. 不用担心 WSDL 中的 URL。 只需在客户端使用
    Url
    属性
    ,就像在这种情况下:
    public class MyWebService : RemoteService
    {
        public MyWebService() : base()
        {
            Url = ConfigurationManager.AppSettings["MyServiceCustomUrl"];
        }
    }

明显的缺点:很可能您将无法使用生成的 html(我的意思是为 Web 方法生成的表单)正确测试服务。

  1. 使用自定义
    wsdlHelpGenerator

我没有测试这个解决方案,但乍一看,如果您基于原始的 DefaultWsdlHelpGenerator.aspx 创建自己的解决方案,它看起来很简单(您可以在 C:\Windows\Microsoft.NET\Framework* 文件夹中找到它) ,就我而言,它是 C:\Windows\Microsoft.NET\Framework 4.0.30319\Config\DefaultWsdlHelpGenerator.aspx)。

  1. 使用
    soapExtensionReflectorTypes
    。你已经这么做了。

缺点:无法控制 HttpPost/HttpGet 协议。

  1. 实现IIS重写模块。它将允许您修改服务产生的任何输出。可以与#3一起使用。

创建2个类,流装饰器:

    public class StreamWatcher : Stream
    {
        private readonly Stream _base;
        private readonly MemoryStream _memoryStream = new MemoryStream();

        public StreamWatcher(Stream stream)
        {
            _base = stream;
        }

        public override void Flush()
        {
            _base.Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _base.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _memoryStream.Write(buffer, offset, count);
            _base.Write(buffer, offset, count);
        }

        public override string ToString()
        {
            return Encoding.UTF8.GetString(_memoryStream.ToArray());
        }

        public override bool CanRead => _base.CanRead;

        public override bool CanSeek => _base.CanSeek;

        public override bool CanWrite => _base.CanWrite;

        public override long Seek(long offset, SeekOrigin origin) => _base.Seek(offset, origin);

        public override void SetLength(long value) => _base.SetLength(value);

        public override long Length => _base.Length;

        public override long Position
        {
            get => _base.Position;
            set => _base.Position = value;
        }
    }

和模块:

    public class WsdlFixHttpModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.EndRequest += (s, e) => OnEndRequest(s, e);
            context.BeginRequest += (s, e) => OnBeginRequest(s, e);
        }

        private void OnBeginRequest(object sender, EventArgs e)
        {
            HttpContext httpContext = HttpContext.Current;
            if (httpContext.Request.Url.Query.Equals("?WSDL", StringComparison.InvariantCultureIgnoreCase)
                || IsAsmxGetRequest(httpContext))
            {
                httpContext.Response.Filter = new StreamWatcher(httpContext.Response.Filter);
            }
        }

        private void OnEndRequest(object sender, EventArgs e)
        {
            HttpContext httpContext = HttpContext.Current;
            string oldValue = "", newValue = "";
            bool replacementRequired = false;

            if (httpContext.Request.Url.Query.Equals("?WSDL", StringComparison.InvariantCultureIgnoreCase))
            {
                oldValue = ":address location=\"http://";
                newValue = ":address location=\"https://";
                replacementRequired = true;
            }
            else if (IsAsmxGetRequest(httpContext))
            {
                oldValue = "<form target=\"_blank\" action='http://";
                newValue = "<form target=\"_blank\" action='https://";
                replacementRequired = true;
            }

            if (replacementRequired)
            {
                string wsdl = httpContext.Response.Filter.ToString();
                wsdl = wsdl.Replace(oldValue, newValue);
                httpContext.Response.Clear();
                httpContext.Response.Write(wsdl);
                httpContext.Response.End();
            }
        }

        private static bool IsAsmxGetRequest(HttpContext httpContext)
        {
            return httpContext.Response.ContentType == "text/html"
                   && httpContext.Request.CurrentExecutionFilePathExtension.Equals(".asmx", StringComparison.InvariantCultureIgnoreCase)
                   && httpContext.Request.HttpMethod == "GET";
        }

        public void Dispose()
        {
        }
    }

然后在web.config中注册模块:

  <system.webServer xdt:Transform="Insert">
    <modules>
      <!-- Required to replace http in addresses for HttpPost/HttpGet protocols -->
      <add name="WsdlFixHttpModule" type="MyWebServices.WsdlFixHttp.WsdlFixHttpModule, MyWebServices"/>
    </modules>
  </system.webServer>

请注意,上面的注册是集成模式,经典模式需要在里面注册

system.web

因此,在我们的遗留项目中,我使用了 3+4 方法,但如果我想再次这样做,我会尝试使用#2 方法。 :)

© www.soinside.com 2019 - 2024. All rights reserved.