tags:

views:

1364

answers:

4

Hi,

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.

A: 

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.

Darrel Miller
Hi Darrel,I tried your suggestion and ran into a few problems.When I used your exact code, I got this error (at compile time):Cannot convert type 'System.ServiceModel.Web.WebOperationContext' to 'System.ServiceModel.OperationContext'And when I changed it to this code:string body = OperationContext.Current.RequestContext.RequestMessage.ToString();The body was an empty string at runtime.Any ideas?Thanks,Uri
urini
A: 

Best way i think doesn't involve WebOperationContext

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

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

Haw-Bin
A: 

Use

OperationContext.Current.RequestContext.RequestMessage

sajith