Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

WCF REST Service for Raw XML

5.00/5 (4 votes)
3 May 2016CPOL1 min read 40K  
Receive raw XML in WCF service

Introduction

I finally found the solution for a rather expected feature in a web service: receive raw XML in a WCF service. This is pretty useful when using contracts is not possible or must be avoided.

Using the Code

Here is a demonstration with 2 projects (VS 2013), one console app to consume the WCF REST Service and send an XML from a file and a WCF service application implemented as a REST service.

1. WCF Service Application

First, a custom mapper for the binding is needed to set the format to „RAW“ for everything coming as XML, so add this to the WCF service project:

C#
/// <summary>
/// class to set raw xml format
/// </summary>
public class RawContentTypeMapper : WebContentTypeMapper
{
    public override WebContentFormat GetMessageFormatForContentType(string contentType)
    {
        try
        {
            if (contentType.Contains("text/xml") ||
                contentType.Contains("application/xml"))
            {
                return WebContentFormat.Raw;
            }
            else
            {
                return WebContentFormat.Default;
            }
        }
        catch (Exception ex)
        { }
        return WebContentFormat.Default;
    }
}

The method for receiving data and sending response to the client (Stream is received and sent back):

C#
/// <summary>
/// receive RAW XML data and send response
/// </summary>
/// <param name="data"></param>
/// <returns>xml response</returns>
public Stream PostData(Stream data)
{

    byte[] response = null;

    try
    {
        //get query string parameters
        NameValueCollection coll =
            WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;
        //get the System from query string
        string sys = coll["System"].ToUpper();

        //read data
        StreamReader sr = new StreamReader(data);
        var request = sr.ReadToEnd();
        sr.Close();

        //TODO : do smth w.request
        response = System.Text.Encoding.Default.GetBytes(request);

    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    //send response
    return new MemoryStream(response);
}

In order to have access to the HTTP context, decorate the WCF class with ASPNet Compatibility attributes:

C#
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TestWCF : ITestWCF
{
    /// <summary>
    /// receive RAW XML data and send response
    /// </summary>
    /// <param name="data"></param>
    /// <returns>xml response</returns>
    public Stream PostData(Stream data)
    {....

The contract is in the expected form:

C#
[ServiceContract]
public interface ITestWCF
{

  [OperationContract]
  Stream PostData(Stream data);
}

In web.config, the following changes are needed: the custom mapper declared for the endpoint and ASPNET compatibility set to true if access to HTTP context is needed:

XML
<system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="webBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="Default">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" 
    multipleSiteBindingsEnabled="true" />
    <services>
      <service behaviorConfiguration="Default" name="TESTWCFService.TestWCF">
        <endpoint address="" behaviorConfiguration="webBehavior" 
        contract="TESTWCFService.ITestWCF" binding="customBinding" 
        bindingConfiguration="CustomMapper" />
      </service>
    </services>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <bindings>
      <customBinding>
        <binding name="CustomMapper">
          <webMessageEncoding webContentTypeMapperType="TESTWCFService.RawContentTypeMapper, 
          TESTWCFService" />
          <httpTransport manualAddressing="true" 
          maxReceivedMessageSize="524288000" transferMode="Streamed" />
        </binding>
      </customBinding>
    </bindings>
  </system.serviceModel>

2. Client Application for Testing

Console application that reads an XML file from disk and sends it to the WCF REST service. Notice that no contracts or references are needed, only the service address, method to call and optional query string parameters. Any changes to the XML will be automatically received at the other end.

C#
class Program
{
    static void Main(string[] args)
    {
        SendXml();
    }
    private static void SendXml()
    {
        HttpWebRequest req = null;
        HttpWebResponse resp = null;

        string baseAddress = "http://localhost:51234/TestWCF.svc";

        try
        {
            string fileName = @"D:\Temp\test.xml";
            string qs = "?System=TEST";
            req = (HttpWebRequest)WebRequest.Create(baseAddress + "/PostData" + qs);

            req.Method = "POST";
            req.ContentType = "application/xml";
            req.Timeout = 600000;

            StreamWriter writer = new StreamWriter(req.GetRequestStream());

            writer.WriteLine(GetTextFromXMLFile(fileName));
            writer.Close();

            Encoding enc = System.Text.Encoding.GetEncoding("utf-8");
            resp = req.GetResponse() as HttpWebResponse;
            Console.WriteLine("HTTP/{0} {1} {2}",
            resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);

            StreamReader sr = new StreamReader(resp.GetResponseStream(), enc);
            string result = sr.ReadToEnd();
            Console.WriteLine(result);
            sr.Close();
        }
        catch (WebException webEx)
        {
            Console.WriteLine("Web exception :" + webEx.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception :" + ex.Message);
        }
        finally
        {
            if (req != null)
                req.GetRequestStream().Close();
            if (resp != null)
                resp.GetResponseStream().Close();
        }
    }
    private static string GetTextFromXMLFile(string file)
    {
        StreamReader reader = new StreamReader(file);
        string ret = reader.ReadToEnd();
        reader.Close();
        return ret;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)