0
votes

I am building a set of Rest Services with WCF. I am using Postman to make the requests for testing. Everything was going fine until I wanted to specify "Content-Type" in the request header.

Service Contract:

[OperationContract]
  [WebInvoke(Method = "POST",
              UriTemplate = "data")]
  Stream GetData(Stream iBody);

And the code behind it:

public Stream GetData(Stream iBody) {
     StreamReader objReader = new StreamReader(iBody);
     string strBody = objReader.ReadToEnd();

     XmlDocument objDoc = new XmlDocument();
     objDoc.LoadXml(strBody);

     return GetStreamData("Hello There. " + objDoc.InnerText);
}

private Stream GetStreamData(string iContent) {
     byte[] resultBytes = Encoding.UTF8.GetBytes(iContent);
     return new MemoryStream(resultBytes);
}

This all works fine as long as I don't include "Content-Type" with the value of "text/xml" in the request headers.

With: enter image description here

Without: enter image description here

I have also tried combinations of "text/xml; charset=utf-8" and "application/xml" to no avail. Does it have anything to do with the type the service method is accepting? Any pointers would be greatly appreciated.

1

1 Answers

4
votes

Since you're using the raw programming model in the WCF service (using Stream as a parameter / return type), you need to tell the WCF stack not to try to interpret incoming requests with XML content-type as XML, and instead just let it pass through. You can do that with a WebContentTypeMapper, which would map all incoming requests, regardless of their content type, to the raw mode. The blog post linked at the top has more information about this, and the code below shows an example of a mapper that handles your case.

public class StackOverflow_35750073
{
    [ServiceContract]
    public class Service
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "data")]
        public Stream GetData(Stream iBody)
        {
            StreamReader objReader = new StreamReader(iBody);
            string strBody = objReader.ReadToEnd();

            XmlDocument objDoc = new XmlDocument();
            objDoc.LoadXml(strBody);

            return GetStreamData("Hello There. " + objDoc.InnerText);
        }
        private Stream GetStreamData(string iContent)
        {
            byte[] resultBytes = Encoding.UTF8.GetBytes(iContent);
            return new MemoryStream(resultBytes);
        }
    }
    class RawMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {
            return WebContentFormat.Raw;
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint1 = host.AddServiceEndpoint(
            typeof(Service),
            new WebHttpBinding { ContentTypeMapper = new RawMapper() },
            "withMapper");
        endpoint1.Behaviors.Add(new WebHttpBehavior());

        ServiceEndpoint endpoint2 = host.AddServiceEndpoint(
            typeof(Service), 
            new WebHttpBinding(),
            "noMapper");
        endpoint2.Behaviors.Add(new WebHttpBehavior());

        host.Open();
        Console.WriteLine("Host opened");

        var input = "<hello><world>How are you?</world></hello>";
        Console.WriteLine("Using a Content-Type mapper:");
        SendRequest(baseAddress + "/withMapper/data", "POST", "text/xml", input);
        SendRequest(baseAddress + "/withMapper/data", "POST", null, input);

        Console.WriteLine("Without using a Content-Type mapper:");
        SendRequest(baseAddress + "/noMapper/data", "POST", "text/xml", input);
        SendRequest(baseAddress + "/noMapper/data", "POST", null, input);

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
    public static string SendRequest(string uri, string method, string contentType, string body)
    {
        string responseBody = null;

        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
        req.Method = method;
        if (!String.IsNullOrEmpty(contentType))
        {
            req.ContentType = contentType;
        }

        if (body != null)
        {
            byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
            req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
            req.GetRequestStream().Close();
        }

        HttpWebResponse resp;
        try
        {
            resp = (HttpWebResponse)req.GetResponse();
        }
        catch (WebException e)
        {
            resp = (HttpWebResponse)e.Response;
        }

        if (resp == null)
        {
            responseBody = null;
            Console.WriteLine("Response is null");
        }
        else
        {
            Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
            foreach (string headerName in resp.Headers.AllKeys)
            {
                Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
            }
            Console.WriteLine();
            Stream respStream = resp.GetResponseStream();
            if (respStream != null)
            {
                responseBody = new StreamReader(respStream).ReadToEnd();
                Console.WriteLine(responseBody);
            }
            else
            {
                Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
            }
        }

        Console.WriteLine();
        Console.WriteLine("  *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*  ");
        Console.WriteLine();

        return responseBody;
    }
}