Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to include WCF Custom Headers in console Service Host

In my WCF service I was getting 405 method not allowederror and then came across a post which suggest to have the following in Application_BeginRequest of my WCF host:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
                    "Accept, Content-Type,customHeader");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods",
                    "POST,GET,OPTIONS");

        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age",
                    "172800");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials",
                    "true");

        HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers",
                    "customHeader");

        HttpContext.Current.Response.AddHeader("Content-type",
                    "application/json");

        HttpContext.Current.Response.End();
    }
    else
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
                    "Accept, Content-Type,customHeader");

        HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers",
                    "customHeader");

        HttpContext.Current.Response.AddHeader("Content-type",
                    "application/json");
    }
} 

But I am hosting my service using a console application.

using (ServiceHost sc = new ServiceHost(typeof(DataRetriever)))
{
    sc.Open();

    foreach (var endPoints in sc.Description.Endpoints)
    {
        Console.WriteLine(endPoints.Address);
    }

    Console.ReadKey();
    sc.Close();
}

So how do I include the headers in the console app.

like image 567
Simsons Avatar asked May 06 '14 11:05

Simsons


People also ask

How to access headers in WCF?

I'm exactly in the same situation, have you found a solution? In WCF, headers can be accessed via an instance of the class OperationContext, which is accessible via the OperationContext.Current (when available). The naive way to approach this problem is to simply use this property within the method of your service:

How do I use a custom service host in WCF?

Using a custom service host in self-host scenarios is straightforward, because it is your application code that is ultimately responsible for creating and opening the service host instance. In the IIS or WAS hosting environment, however, the WCF infrastructure is dynamically instantiating your service's host in response to incoming messages.

What is the WCF hosting environment?

In the IIS or WAS hosting environment, however, the WCF infrastructure is dynamically instantiating your service's host in response to incoming messages. Custom service hosts can also be used in this hosting environment, but they require some additional code in the form of a ServiceHostFactory.

How to use WCF service with windows and Linux client?

Here, you can say that WCF Service will communicate with both the Windows and Linux client. First, we make one simple WCF Service for “addition” and host it on console application; then, consume it from one Windows Form application. Open Visual Studio instance with "Run as administrator" and create one class library project. Name it “MathService”.


1 Answers

In WCF, headers can be accessed via an instance of the class OperationContext, which is accessible via the OperationContext.Current (when available).

The naive way to approach this problem is to simply use this property within the method of your service:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    void MyMethod();
}

internal class MyService: IMyService
{
    public void MyMethod()
    {
        Console.WriteLine("My Method");
        OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("headerFromMethod", "namespace", "headerFromMethodValue"));
    }
}

For completeness, the code used to host this service within the Console Application (no config required) is:

using (var serviceHost = new ServiceHost(typeof(MyService)))
{
    var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000");

    serviceHost.Open();

    Console.WriteLine("Open for business");
    Console.ReadLine();

    serviceHost.Close();
}

A .NET client would access the headers like this:

var channel = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress("http://localhost:9000"));

var contextChannel = channel as IContextChannel;
using (new OperationContextScope(contextChannel))
{
    channel.MyMethod();

    var incommingHeaders = OperationContext.Current.IncomingMessageHeaders;
    var header = incommingHeaders.GetHeader<string>("headerFromMethod", "namespace");
    Console.WriteLine("Header from server: " + header);
}

If you have Fiddler, you can also see the headers using this tool.

Whilst this method will do what you want, it is questionable whether you want to mix your business logic (contained within the implementation of IMyService), and the logic controlling the "out-of-band" information attached to the message.

A cleaner separation is gained by implementing IDispatchMessageInspector, which allows you to intercept calls on the server and modify the message as it comes in and out:

public class ServerInterceptor: IDispatchMessageInspector, IEndpointBehavior
{
    object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        return null;
    }

    void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
    {
        reply.Headers.Add(MessageHeader.CreateHeader("header", "namespace", "headervalue"));
    }

    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
    }

    void IEndpointBehavior.Validate(ServiceEndpoint endpoint){}

    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}

    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime){}
}

The headers are accessed from a .NET client in the same way as before. It's worth noting that you can pass information from the AfterReceiveRequest method to the BeforeSendReply, as the object returned in the former method is passed as the correlationState parameter in the latter. This would be useful if the headers you return are dependent on the headers of the incoming message - as your example suggests.

Finally, to install this functionality on the service, you need to modify the hosting code as follows:

...
var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000");
endpoint.Behaviors.Add(new ServerInterceptor());
serviceHost.Open();
...

which we can do by virtue of the fact that ServerInterceptor implements IEndpointBehavior

like image 164
Lawrence Avatar answered Oct 24 '22 01:10

Lawrence