WCF: Not able to download metadata from 3rd party web service

Problem statement

There is a publicly available 3rd party web service. The objective is to consume this service as a normal WCF service client for .net application. However, application gets into error: ‘Cannot obtain metadata using WS-Metadata Exchange or DISCO’. It all happens during metadata download or add service reference option. 

Issue

During the metadata download, you get into error:

C:\Temp>svcutil /t:metadata <localhost/service1>

Microsoft (R) Service Model Metadata Tool

[Microsoft (R) Windows (R) Communication Foundation, Version 4.0.30319.18020]

Copyright (c) Microsoft Corporation. All rights reserved.

 

Attempting to download metadata from <localhost/service1>' using WS-Metadata Exchange or DISCO.

Microsoft (R) Service Model Metadata Tool

[Microsoft (R) Windows (R) Communication Foundation, Version 4.0.30319.18020]

Copyright (c) Microsoft Corporation. All rights reserved.

 

Error: Cannot obtain Metadata from localhost/service1

 

If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation at go.microsoft.com/fwlink/?LinkId=65455.

WS-Metadata Exchange Error

    URI: <localhost/service1>

    Metadata contains a reference that cannot be resolved: ‘localhost/service1’.

    The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '<pre>Traceback (most recent call last):

<<<<<<Internal callstack of service application>>>>>>

                line 123, in parse

                self.feed(buffer)

                File &quot;/base/data/home/runtimes/python/p'.

 

    The remote server returned an error: (500) Internal Server Error.

HTTP GET Error

    URI: <localhost/service1>

    There was an error downloading '<localhost/service1>'.

    The request failed with the error message:

--

<?xml version="1.0" encoding="utf-8"?>

<definitions xmlns="schemas.xmlsoap.org/wsdl/" xmlns:soap="schemas.xmlsoap.org/wsdl/soap/" xmlns:s="www.w3.org/2001/XMLSchema" xmlns:tns="<localhost/service1>" xmlns:soapenc="schemas.xmlsoap.org/soap/encoding/" xmlns:mime="schemas.xmlsoap.org/wsdl/mime/" targetNamespace="<localhost/service1>”>

<<<<Other service wsdl items>>>>

</definitions>

--.

If you would like more help, type "svcutil /?"

 

In any service related cases,

1. Client application should be able to download metadata.

2. After proxy definition of the service is downloaded to the client side, client will form request and dispatch it over the wire.

Over here, it fails at the first step. And, it is clear from the highlighted error.

 

What could be the issue?

If we keenly look into the error, there is a “500 internal server error along with text/html content type”. It means service application is itself broken. Client has no control over service.

Even then, client application looks for a way to consume service. Is it possible?

Answer – Yes

How can it be achieved?

Step-1

1.      Use SOAP UI tool (<www.soapui.org/>)

2.       Consume the web service (In most of the cases, it will work even when there is an issue with service application)

3.       Hit service and see if it works.

4.       In my case, it was successful.

5.       From this step, we are able to identify the HTTP request payload to send for the service request.

Step-2

1.       Prepare the SOAP envelope and convert into XML document.

2.       Create System.Net.HttpWebRequest object and add the metadata information to it.

3.       Read the HttpWebRequest stream and attach SOAP envelope XML to the stream.

4.       Read the System.Net.WebResponse object

5.       Convert webResponse to stream and read the result. However, the result will be a string (i.e. response payload).

Client application code (Console application)

       static void Main(string[] args)        {

            CallService(); 

            Console.ReadLine();

        } 

        private static void CallService()

        {

            var _url = "<localhost/service1>";

            var _action = "<localhost/service1>/GetQuestionsAbout";

             XmlDocument soapEnvelopeXml = CreateSoapEnvelope();

            HttpWebRequest webRequest = CreateWebRequest(_url, _action);

            InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);

             string soapResult;

            using (WebResponse webResponse = webRequest.GetResponse())

            {

                using (StreamReader rd = new StreamReader(webResponse.GetResponseStream()))

                {

                    soapResult = rd.ReadToEnd();

                }

                Console.Write(soapResult);

            }

        }

         private static void InsertSoapEnvelopeIntoWebRequest(XmlDocument soapEnvelopeXml, HttpWebRequest webRequest)

        {

            using (Stream stream = webRequest.GetRequestStream())

            {

                soapEnvelopeXml.Save(stream);

            } 

        } 

        private static XmlDocument CreateSoapEnvelope()

        {

            XmlDocument soapEnvelop = new XmlDocument();

            Console.Write("Query for ?? ");

            string queryItem = Console.ReadLine();

             // Pull this xml from SOAP UI or service provider

            soapEnvelop.LoadXml(@"<soapenv:Envelope xmlns:soapenv=""schemas.xmlsoap.org/soap/envelope/"" xmlns:soap=""<localhost/service1>""><soapenv:Header/><soapenv:Body><soap:GetQuestionsAbout><soap:query>" + queryItem + "</soap:query></soap:GetQuestionsAbout></soapenv:Body></soapenv:Envelope>");

            return soapEnvelop;

        }

         private static HttpWebRequest CreateWebRequest(string url, string action)

        {

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);

            webRequest.Headers.Add("SOAPAction", action);

            webRequest.ContentType = "text/xml;charset=\"utf-8\"";

            webRequest.Accept = "text/xml";

            webRequest.Method = "POST";

            return webRequest;

        }

 

 

I hope this helps!