Rogerio Torres
Rogerio Torres

Reputation: 73

How to change the address location of my wsdl

My wsdl put a wrong domain in address location, How to fix it?

- <wsdl:service name="XWebService">
- <wsdl:port name="XServiceSoap" binding="tns:XWebServiceSoap">
  <soap:address location="https://machine.wrongdomain.com.br/webservices/MapleStoryWebService.asmx" /> 
  </wsdl:port>
- <wsdl:port name="XWebServiceSoap12" binding="tns:XWebServiceSoap12">
  <soap12:address location="https://machine.wrongdomain.com.br/webservices/XWebService.asmx" /> 
  </wsdl:port>
- <wsdl:port name="XWebServiceHttpGet" binding="tns:XWebServiceHttpGet">
  <http:address location="https://machine.wrongdomain.com.br/webservices/MapleStoryWebService.asmx" /> 
  </wsdl:port>
- <wsdl:port name="XWebServiceHttpPost" binding="tns:XWebServiceHttpPost">
  <http:address location="https://machine.wrongdomain.com.br/webservices/XWebService.asmx" /> 
  </wsdl:port>
  </wsdl:service>

The true domain is like https://machine.goodDomain.com.br

Upvotes: 3

Views: 20857

Answers (5)

Vitaly Filatenko
Vitaly Filatenko

Reputation: 496

There are few solution, I will sort them from simplicity to accuracy. :)

  1. Don't worry about URLs in WSDL. Just use Url property on a client side, like in that case:
    public class MyWebService : RemoteService
    {
        public MyWebService() : base()
        {
            Url = ConfigurationManager.AppSettings["MyServiceCustomUrl"];
        }
    }

Obvious drawback: most probably you will not able to test the service properly using generated html (I mean generated forms for web methods).

  1. Use custom wsdlHelpGenerator.

I didn't test this solution, but at a glance it looks simply enough if you create your own based on original DefaultWsdlHelpGenerator.aspx (you can find it in C:\Windows\Microsoft.NET\Framework* folders, in my case it was C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\DefaultWsdlHelpGenerator.aspx).

  1. Use soapExtensionReflectorTypes. It will allow you to do anything with your HttpSoap protocol addresses.

Create a handler (sample below will change http to https):

    public class HttpsReflector : SoapExtensionReflector
    {
        public override void ReflectMethod()
        {
            // nothing to override
        }

        public override void ReflectDescription()
        {
            ServiceDescription description = ReflectionContext.ServiceDescription;
            foreach (System.Web.Services.Description.Service service in description.Services)
            {
                foreach (Port port in service.Ports)
                {
                    foreach (ServiceDescriptionFormatExtension extension in port.Extensions)
                    {
                        if (extension is SoapAddressBinding binding)
                        {
                            binding.Location = binding.Location.Replace("http://", "https://");
                        }
                    }
                }
            }
        }
    }

Register it in web.config:

  <system.web>
    <webServices>
      <soapExtensionReflectorTypes>
        <!-- Required to replace http in addresses for HttpSoap protocol -->
        <add type="MyWebServices.WsdlFixHttp.HttpsReflector, MyWebServices"/>
      </soapExtensionReflectorTypes>
    </webServices>
  </system.web>

A drawback: no control over HttpPost/HttpGet protocols.

  1. Implement IIS rewrite module. It will allow you to modify any output produced by your service. Can be used together with #3.

Create 2 classes, stream decorator:

    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;
        }
    }

and the module:

    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()
        {
        }
    }

Then register the module in 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>

Please note, the registration above is for integration mode, for classic one you need to register it inside system.web.

So, in our legacy project I used 3+4 approach, but if I'd want to do that again, I will try to use #2 approach instead. :)

Upvotes: 0

Robert Pilcher
Robert Pilcher

Reputation: 406

Another option is to use the IIS URL Rewrite module (http://www.iis.net/downloads/microsoft/url-rewrite)

First up - capture the output from XWebService.asmx?WSDL and save it as an HTML file (e.g. wsdl.htm).

Edit this file and change the location to the alternative address

... from thishost.domain.com:

- <wsdl:port name="XWebServiceHttpPost" binding="tns:XWebServiceHttpPost">
  <http:address location="http://thishost.domain.com/XWebService.asmx" /> 
  </wsdl:port>

...to thathost.domain.com:

- <wsdl:port name="XWebServiceHttpPost" binding="tns:XWebServiceHttpPost">
  <http:address location="http://thathost.domain.com/XWebService.asmx" /> 
  </wsdl:port>

In IIS - find the URL Rewrite icon in the website/virtual Feature View . Then click Add Rule(s) and choose Inbound Rule - Blank rule.

With the rule - name it appropriately and set the pattern match for the webservice URL that will receive the WSDL request. For RegEx:

(.*)XWebservice.asmx

For the conditions match {QUERY_STRING} to WSDL and {REQUEST_METHOD} to GET.

For the Action - set it to Rewrite (so this is transparent to the client) and choose the file that we saved it as earlier (wsdl.htm).

This also adds a new rewrite section to the system.webServer section of the web.config

<system.webServer>
    <rewrite>
        <rules>
            <rule name="WSDL Rewrite" stopProcessing="true">
                <match url="(.*)XWebService.asmx" />
                <conditions>
                    <add input="{QUERY_STRING}" pattern="WSDL" />
                    <add input="{REQUEST_METHOD}" pattern="GET" />
                </conditions>
                <action type="Rewrite" url="wsdl.htm" />
            </rule>
        </rules>
    </rewrite>
</system.webServer>

Upvotes: 2

Mark Meuer
Mark Meuer

Reputation: 7513

Why not just manually edit the address in the WSDL file to what it should be?

If the WSDL is generated by some other tool, then let us know how it is being generated and perhaps we can help. Otherwise, there is no law against modifying the generated file to suit your needs. If all that is wrong with the WSDL for the original users's environment is that the URL is wrong then it is perfectly legitimate to modify the URL directly.

Upvotes: 1

Jared
Jared

Reputation: 2015

The initial response is correct. The default URL in the WSDL is dependent upon the URL used to access the WSDL.

The way my team handled changing service URLs in the past (for instance, transitioning from a development to a testing or production environment) is to use wsdl.exe to generate a code proxy for your web service (a proxy is actually created by making a web or service reference as well, but is not displayed in Visual Studio by default), you can edit the generated proxy class to read the service's URL from (??) wherever you want to store it - database, config file etc.

Upvotes: 0

James Bailey
James Bailey

Reputation: 1245

The address is taken from the URL used to access the WSDL. If it's different than the server you want to actually serve from, then you could change it by creating a class that extends SoapExtensionReflector. Here's a sample article on how to change the URL:

http://blogs.msdn.com/kaevans/archive/2005/11/16/493496.aspx

Upvotes: 4

Related Questions