A day with .Net

My day to day experince in .net

WCF REST Service accepting Raw Xml- WebContentTypeMapper

Posted by vivekcek on June 14, 2012

My Problem
———-

I want to develop a WCF-REST service that accept XML through POST method. Process the xml and send an xml response back. Different types of xml string are posted to my service. My service need to accept any format like text/xml,appilcation/xml etc.

How i am going to solve
————————————–

I am planning to use .NET 4.0 for my WCF service. In .NET 4.0 WCF support Raw data in the form of Stream.

To Code
——-

1. Create a WCF Service application.

2. In the Service contract write below code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using System.IO;
using System.Xml.Linq;
namespace RestRaw
{    
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "Push",
               RequestFormat = WebMessageFormat.Xml,
               ResponseFormat = WebMessageFormat.Xml,
               BodyStyle = WebMessageBodyStyle.Bare)]
        XElement DoWork(Stream xml);
    }
}

3. In the Service implementation write the following code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IO;
using System.Xml.Linq;
namespace RestRaw
{
   
    public class Service1 : IService1
    {
        public XElement DoWork(Stream xml)
        {
            StreamReader reader = null;
            XDocument xDocRequest = null;
            string strXmlRequest = string.Empty;
            reader = new StreamReader(xml);
            strXmlRequest = reader.ReadToEnd();
            xDocRequest = XDocument.Parse(strXmlRequest);
            string response = "<Result>OK</Result>";
            return XElement.Parse(response);
        }
    }
}

4. Now create a class named RawContentTypeMapper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace RestRaw
{
    public class RawContentTypeMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {

            if (contentType.Contains("text/xml") || contentType.Contains("application/xml"))
            {

                return WebContentFormat.Raw;

            }

            else
            {

                return WebContentFormat.Default;

            }
        }
    }
}

5. Change your Web.config as below.

<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="" name="RestRaw.Service1">
        <endpoint address="" behaviorConfiguration="web" contract="RestRaw.IService1" binding="customBinding" bindingConfiguration="RawReceiveCapable"></endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception 

information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    <bindings>
      <customBinding>
        <binding name="RawReceiveCapable">
          <webMessageEncoding webContentTypeMapperType="RestRaw.RawContentTypeMapper, RestRaw, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <httpTransport manualAddressing="true" maxReceivedMessageSize="524288000"
           transferMode="Streamed" />
        </binding>

      </customBinding>
    </bindings>
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  
</configuration>

6. Code to test service

 private static string PostXml(string xml, string url)
        {
            // Restful service URL
            //string url = txtURL.Text;

            // declare ascii encoding
            ASCIIEncoding encoding = new ASCIIEncoding();
            string strResult = string.Empty;
            // sample xml sent to Service & this data is sent in POST
            string SampleXml = xml;
            string postData = SampleXml.ToString();
            // convert xmlstring to byte using ascii encoding
            byte[] data = encoding.GetBytes(postData);
            // declare httpwebrequet wrt url defined above
            HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(url);
            // set method as post
            webrequest.Method = "POST";
            // set content type
            webrequest.ContentType = "text/xml";
            // set content length
            webrequest.ContentLength = data.Length;
            // get stream data out of webrequest object
            Stream newStream = webrequest.GetRequestStream();
            newStream.Write(data, 0, data.Length);
            newStream.Close();
            // declare & read response from service
            HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();

            // set utf8 encoding
            Encoding enc = System.Text.Encoding.GetEncoding("utf-8");
            // read response stream from response object
            StreamReader loResponseStream =
        new StreamReader(webresponse.GetResponseStream(), enc);
            // read string from stream data
            strResult = loResponseStream.ReadToEnd();
            // close the stream object
            loResponseStream.Close();
            // close the response object
            webresponse.Close();

            return strResult;
        }

Solution

DOWNLOAD

http://sdrv.ms/M0AunA

Advertisements

19 Responses to “WCF REST Service accepting Raw Xml- WebContentTypeMapper”

  1. Marvin said

    In testing, why am I getting this error “WebException was unhandled. The remote server returnedan error: (404) Not Found.”
    when passing to this part of the code.. HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();
    Please help! Thanks in advance

  2. Marvin said

    Okay I got it. I must update the Web.config by defining the endpoint address.
    But now I’m having a new error. “WebException was unhandled. The remote server returned an error: (500) Internal Server Error.”

    • vivekcek said

      post the exception.Check service using fiddler

      • vivekcek said

        down load this code and use http://sdrv.ms/M0AunA

      • Marvin said

        Yeah, I downloaded your code and that’s what I’m using.

        Here’s what my console test program looks like.

        class Program
        {
        static void Main(string[] args)
        {
        Console.WriteLine(PostXml(“OK”, “http://localhost/RestRaw/Service1.svc”));
        Console.ReadLine();

        }
        private static string PostXml(string xml, string url)
        {
        // Restful service URL
        //string url = txtURL.Text;

        // declare ascii encoding
        ASCIIEncoding encoding = new ASCIIEncoding();
        string strResult = string.Empty;
        // sample xml sent to Service & this data is sent in POST
        string SampleXml = xml;
        string postData = SampleXml.ToString();
        // convert xmlstring to byte using ascii encoding
        byte[] data = encoding.GetBytes(postData);
        // declare httpwebrequet wrt url defined above
        HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(url);
        // set method as post
        webrequest.Method = “POST”;
        // set content type
        webrequest.ContentType = “text/xml”;
        // set content length
        webrequest.ContentLength = data.Length;
        // get stream data out of webrequest object
        Stream newStream = webrequest.GetRequestStream();
        newStream.Write(data, 0, data.Length);
        newStream.Close();
        // declare & read response from service
        HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();

        // set utf8 encoding
        Encoding enc = System.Text.Encoding.GetEncoding(“utf-8”);
        // read response stream from response object
        StreamReader loResponseStream =
        new StreamReader(webresponse.GetResponseStream(), enc);
        // read string from stream data
        strResult = loResponseStream.ReadToEnd();
        // close the stream object
        loResponseStream.Close();
        // close the response object
        webresponse.Close();

        return strResult;

        }

        }

        here’s my web.config from RestRaw project.
        I’ve just updated the endpoint address and set multipleSiteBindingsEnabled to “false”

        And here’s the exception I’m getting

        System.Net.WebException was unhandled
        HResult=-2146233079
        Message=The remote server returned an error: (404) Not Found.
        Source=System
        StackTrace:
        at System.Net.HttpWebRequest.GetResponse()
        at TestService.Program.PostXml(String xml, String url) in D:\PlayingAround\SystemMonitorWebService_test\TestService\Program.cs:line 44
        at TestService.Program.Main(String[] args) in D:\PlayingAround\SystemMonitorWebService_test\TestService\Program.cs:line 14
        at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
        at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
        at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
        at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
        at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
        at System.Threading.ThreadHelper.ThreadStart()
        InnerException:

        Thanks again!

      • Marvin said

        My config didn’t post.
        Again, here’s my web.config.

        Thanks

  3. Marvin said

    Finally!! I was able to make it run!

    I’ve missed the “/Push” at the end of the URL

    Console.WriteLine(PostXml(“OK”, “http://localhost/RestRaw/Service1.svc/PUSH”));

    Thanks!!

  4. Harry said

    I copied all your code, when I compile I receive below exception, any idea what cause this?

    Could not load file or assembly ‘RestRaw, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.

    • Harry said

      Got it working, great blog!.

      • Mohammed Khadeer said

        How you solve the above issue Could not load file or assembly ‘RestRaw, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.

      • vivekcek said

        Check your framework version and library version,
        i recommend create project from scratch, than copying my config file

  5. Harry said

    Thank You 🙂

  6. Rick said

    Hi – thanks a lot for this post – exactly what I needed for some code I am working. Can you tell me how to have this called by Https? When I change the entry in Web.Config for CustomBinding to this, it complains about Mode attribute not being allowed. Thanks R

  7. kk said

    Thanks for the Post. It resolved my issue, raising another 🙂
    New Issue: am using this in a Webservice, while request it converts the text to RAW fine but for response too… it responding back in RAW and the response is in encoded format, how can i get back to normal format (I mean decode it).
    Note: assume the response is a string/stream which is not encoded, but the http response messagebody has encoded version of my return. because of this RAW

    I believe its because of the config <webMessageEncoding ….

    plz Suggest, thanks in advance

    • kk said

      Update:
      response looks like

      DATA
      f72

      Session state has created a session id, but cannot save it because the response was already flushed by the application.

      body bla bla bla

      Want to get rid of the added html in the response messagebody
      Note: tries to place below statement in global.asax.cs – didn’t worked
      string sessionId = Session.SessionID;

  8. Hi , I followed you article but am getting 400 bad request while getting the response. By trace log I found the exception is ServiceModel.CommunicationException and message is Unrecognized Message version. Please find my code below

    Service

    [ServiceContract]
    public interface IXMLFileProcessService
    {
    [OperationContract]
    [WebInvoke(Method = “POST”,
    UriTemplate = “PUSH”,
    RequestFormat = WebMessageFormat.Xml,
    BodyStyle = WebMessageBodyStyle.Bare)]
    XElement ProcessFileDataByFile(Stream document);
    }

    public class XMLProcessService : IXMLFileProcessService
    {

    public XElement ProcessFileDataByFile(Stream document)
    {
    Logger.WriteLog(LogLevel.INFO, “Call Started-> ProcessFileDataByFile”);
    StreamReader reader = null;
    XDocument xDocRequest = null;
    string strXmlRequest = string.Empty;
    reader = new StreamReader(document);
    strXmlRequest = reader.ReadToEnd();
    xDocRequest = XDocument.Parse(strXmlRequest);
    string response = “OK”;
    Logger.WriteLog(LogLevel.INFO, “Call Ended-> ProcessFileDataByFile”);
    return XElement.Parse(response);
    }
    }

    Client

    string serviceUrl = “http://” + Environment.MachineName + “/FileReadPOC/XMLProcessService.svc”;

    XmlDocument document = new XmlDocument();
    document.Load(@”D:\My Projects\SampleXML_1.xml”);
    string xml = document.InnerXml;

    ASCIIEncoding encoding = new ASCIIEncoding();
    string strResult = string.Empty;
    string SampleXml = xml;
    string postData = SampleXml.ToString();
    byte[] data = encoding.GetBytes(postData);
    HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(serviceUrl);

    webrequest.Method = “POST”;

    webrequest.ContentType = “text/xml;charset=utf-8”;

    webrequest.ContentLength = data.Length;
    Stream newStream = webrequest.GetRequestStream();
    newStream.Write(data, 0, data.Length);
    newStream.Close();
    HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse();

    Encoding enc = System.Text.Encoding.GetEncoding(“utf-8”);
    StreamReader loResponseStream = new StreamReader(webresponse.GetResponseStream(), enc);
    strResult = loResponseStream.ReadToEnd();
    loResponseStream.Close();
    webresponse.Close();

    Please Help

  9. Kdud said

    You are one Legend… Keep up the good work

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s