Access Request Body in a WCF RESTful Service

How do I access the HTTP POST request body in a WCF REST service?

Here is the service definition:

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
    MyData GetData();
}

Here is the implementation:

public MyData GetData()
{
    return new MyData();
}

I though of using the following code to access the HTTP request:

IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;

But the IncomingWebRequestContext only gives access to the headers, not the body.

Thanks.

Answers


Best way i think doesn't involve WebOperationContext

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);

Use

OperationContext.Current.RequestContext.RequestMessage


Sorry for the late answer but I thought I would add what works with UriTemplate parameters to get the request body.

[ServiceContract]
public class Service
{        
    [OperationContract]
    [WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")]
    public Stream TestPost(string param0, string param1)
    {

        string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());

        return ...;
    }
}

body is assigned a string from the raw bytes of the message body.


It seems that because WCF is designed to be transport protocol-agnostic, a service method doesn't provide access to HTTP-specific information by default. However, I just came across a nice article describing "ASP.Net Compatibility Mode" which essentially allows you to specify that your service is indeed intended to be exposed via HTTP.

http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx

Adding the aspNetCompatibilityEnabled configuration to Web.config, combined with the AspNetCompatibilityRequirements attribute to the desired service operations, should do the trick. I'm about to try this myself.

Haw-Bin


The above answers helped me come up with this solution. I am receiving json with name/value pairs. {"p1":7514,"p2":3412, "p3":"joe smith" ... }

[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
    [WebInvoke(Method = "POST",
        BodyStyle = WebMessageBodyStyle.Bare, 
        RequestFormat = WebMessageFormat.Json
        )]

public Stream getJsonRequest()
    {

        // Get the raw json POST content.  .Net has this in XML string..
        string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString();

        // Parse the XML string into a XML document
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(JSONstring);

        foreach (XmlNode node in doc.DocumentElement.ChildNodes)
        {
                node.Name // has key
                node.InnerText;  // has value

My apologies for the previous answer, I stupidly assumed that I had just cast WebOperationContext to get at the OperationContext, unfortunately the real answer is much more ugly.

Let me preface this with, there must be a better way!

First I created my own context object, that could be attached to the existing OperationContext object.

public class TMRequestContext : IExtension<OperationContext>  {

    private OperationContext _Owner;

        public void Attach(OperationContext owner) {
            _Owner = owner;
        }

     public void Detach(OperationContext owner) {
            _Owner = null;
        }

    public static TMRequestContext Current {
            get {
                if (OperationContext.Current != null) {
                    return OperationContext.Current.Extensions.Find<TMRequestContext>();
                } else {
                    return null;
                }
            }
        }
}

In order to be able to access this new context object, you need to add it as an extension to the current one. I did that by creating a message inspector class.

public class TMMessageInspector : IDispatchMessageInspector {

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {

            OperationContext.Current.Extensions.Add(new TMRequestContext());
            return null;
        }
}

In order for the message inspector to work you need to create a new "behaviour". I did this using the following code.

    public class TMServerBehavior : IServiceBehavior {

        public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
            //Do nothing
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) {

            foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {

                foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
                    epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector());
                }
            }

        }
}

The behaviour you should be able to add in the config file, although I did it by creating a new host and adding the behaviour object manually in the OnOpening method. I ended up using these class for much more than just accessing the OperationContext object. I used them for logging and overriding the error handling and access to the http request object,etc. So, it is not quite as ridiculous solution as it seems. Almost, but not quite!

I really don't remember why I could not just access OperationContext.Current directly. I have a faint recollection that it was always empty and this nasty process was the only way I could get an instance that actually contained valid data.


Need Your Help

How to automate SQL backup on a shared hosted server?

sql sql-server backup

I'm using a hosting service which allows me to backup my SQL 2008 database and download the BAK file via a web interface only--and I have access to the database via Management Studio. I can execute...

MVC View, Table Loop Query

c# asp.net-mvc loops view html-table

This is what I'm doing to achieve this view inside table using static data