Where do I set the CookieContainer on a Service Reference?

user677607 picture user677607 · Aug 23, 2013 · Viewed 7.8k times · Source

When adding WebService Reference to an ASMX Service on a .NET 2.0 project for example,

var objService = new NameSpace.groupservices();

there exists,

objService.CookieContainer = new System.Net.CookieContainer();

When adding ServiceReference to an ASMX Service on a .NET 4.0 project for example,

var objService = new NameSpace.groupservicesSoapClient();

there isn't any CookieContainer property for objService

A similar question was asked here with no positive solution.

Could someone please guide where to find the property?

Answer

Markus picture Markus · Apr 23, 2014

In contrast to ASMX Web Services that are tied to HTTP transport, WCF allows for various transport protocols to be used. Therefore, not all protocol-specific options (such as Cookies for HTTP transport) are available in a WCF service reference.

You can, however, add a message inspector that inspects the messages that are sent between client and server. This article describes a way to send cookies to the server.

I've extended the sample to use a CookieContainer. Also, the following code shows how to evaluate the Set-Cookie header sent by the server to add the new cookies to the container. Please note that the sample shows a basic outline, but might need extension or some more validation. However, in a simple scenario it worked.

The following snippet shows a test method of a WCF service that is hosted on IIS and integrated in the ASP.NET framework. It basically echoes the cookies sent to the server in a string and adds two new ones:

public string GetData(int value)
{
    var reply = string.Join(", ", 
                    from x in HttpContext.Current.Request.Cookies.AllKeys 
                    select x + "=" + HttpContext.Current.Request.Cookies[x].Value);
    HttpContext.Current.Response.Cookies.Add(new HttpCookie("Test", "Test123"));
    HttpContext.Current.Response.Cookies.Add(new HttpCookie("Test2", "Test1234"));
    return reply;
}

The following test program creates a CookieContainer for the cookies, adds a demo cookie and registers a new behavior for the endpoint of the service:

class Program
{
    static void Main(string[] args)
    {
        var cookieCont = new CookieContainer();
        using(var svc = new TestServiceReference.TestServiceClient())
        {
            cookieCont.Add(svc.Endpoint.Address.Uri, new Cookie("TestClientCookie", "Cookie Value 123"));
            var behave = new CookieBehavior(cookieCont);
            svc.Endpoint.EndpointBehaviors.Add(behave);
            var data = svc.GetData(123);
            Console.WriteLine(data);
            Console.WriteLine("---");
            foreach (Cookie x in cookieCont.GetCookies(svc.Endpoint.Address.Uri))
                Console.WriteLine(x.Name + "=" + x.Value);
        }
        Console.ReadLine();
    }
}

The behavior serves the purpose of adding a custom message inspector and handing over the CookieContainer:

public class CookieBehavior : IEndpointBehavior
{
    private CookieContainer cookieCont;

    public CookieBehavior(CookieContainer cookieCont)
    {
        this.cookieCont = cookieCont;
    }

    public void AddBindingParameters(ServiceEndpoint serviceEndpoint,
        System.ServiceModel.Channels
        .BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint,
        System.ServiceModel.Dispatcher.ClientRuntime behavior)
    {
        behavior.MessageInspectors.Add(new CookieMessageInspector(cookieCont));
    }

    public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint,
        System.ServiceModel.Dispatcher
        .EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint serviceEndpoint) { }
}

The message inspector both adds cookies when a request is sent to the server in the BeforeSendRequest method and retrieves the cookies that should be updated in the AfterReceiveReply method. Note that the correlationState returned by BeforeSendRequest is used to retrieve the Uri in the AfterReceiveReply:

public class CookieMessageInspector : IClientMessageInspector
{
    private CookieContainer cookieCont;

    public CookieMessageInspector(CookieContainer cookieCont)
    {
        this.cookieCont = cookieCont;
    }

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply,
        object correlationState) 
    {
        object obj;
        if (reply.Properties.TryGetValue(HttpResponseMessageProperty.Name, out obj))
        {
            HttpResponseMessageProperty httpResponseMsg = obj as HttpResponseMessageProperty;
            if (!string.IsNullOrEmpty(httpResponseMsg.Headers["Set-Cookie"]))
            {
                cookieCont.SetCookies((Uri)correlationState, httpResponseMsg.Headers["Set-Cookie"]);
            }
        }
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request,
        System.ServiceModel.IClientChannel channel)
    {
        object obj;
        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out obj))
        {
            HttpRequestMessageProperty httpRequestMsg = obj as HttpRequestMessageProperty;
            SetRequestCookies(channel, httpRequestMsg);
        }
        else
        {
            var httpRequestMsg = new HttpRequestMessageProperty();
            SetRequestCookies(channel, httpRequestMsg);
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMsg);
        }

        return channel.RemoteAddress.Uri;
    }

    private void SetRequestCookies(System.ServiceModel.IClientChannel channel, HttpRequestMessageProperty httpRequestMessage)
    {
        httpRequestMessage.Headers["Cookie"] = cookieCont.GetCookieHeader(channel.RemoteAddress.Uri);
    }
}