Where do I set the CookieContainer on a Service Reference?
Asked Answered
T

3

16

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?

Trailer answered 23/8, 2013 at 21:4 Comment(1)
@marc_s: even for an ASMX service, he should be using "Add Service Reference".Tavis
C
16

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);
    }
}
Cyruscyst answered 23/4, 2014 at 7:10 Comment(1)
Important to mention that this is not working if <binding allowCookies="true" /> is set as well!Bowens
H
16

Open your app.config file and add allowCookies="true" to the binding.

Something like this:

<binding allowCookies="true" />
Hamner answered 8/8, 2014 at 22:13 Comment(5)
Given that the top, bounty-winning answer here is so complicated, it seems unlikely this is the answer, but at least with eTapestry's API, which requires cookies to operate, this is all it takes.Raisin
Wow...old answer but perfect. Simplicity wins over insane complexity. I'm working with a SOAP API (InsideSales) that requires a login which sets a session cookie for all future calls. This is all I needed.Playa
Both the answers are best at their place. This answer applies when you need to share a cookie for all operations doing on the same Service Reference. But the bounty winning answer comes in a role when you want to share a cookie across multiple Service References.Ninety
THIS is definitely the correct answer, but it's easy to overlook on Google. So again - for keywords - if you need to maintain a session in a windows application in webservice in a .net application (equivalent to "maintain http session in SoapUI"), just enter this file and paste it there. Nothing more :-)Vernitavernoleninsk
If you manage the cookies yourself you have to set allowCookies="false" as explained here: megakemp.com/2009/02/06/managing-shared-cookies-in-wcfJannelle
B
-1

Found the solution in here:

http://msdn.microsoft.com/en-us/library/bb628649.aspx

It turns out I need a web reference instead of a service reference

Birdiebirdlike answered 23/4, 2014 at 3:10 Comment(2)
Uh, no, you don't. Why do you think you do?Tavis
Agreed, Web References are just the old iteration. Use @Markus' answer, above, insteadPitfall

© 2022 - 2024 — McMap. All rights reserved.