Grabbing Cookies in Web Browser Control - WP7

2019-01-17 14:14发布

问题:

In order to log into a certain part of a website the users of my application require their cookie. To do this I need to grab it and pass it to url.

Does anyone know how to grab a certain websites cookie from the browser control?

I saw this method but wasn't quite clear.

Thanks, TP.

回答1:

As of WP 7.1 Mango "release", if one may call it, please see the WebBrowser Control Overview for Windows Phone. It has been recently updated a little bit, and it turns out that they actually have added some support for cookie-retrieval from the WebBrowser. On the bottom of the page you will find a tiny link GetCookies(WebBrowser) pointing to description of a new class: WebBrowserExtensions with this very handy method. Yes, this class has only that one single member. It's an extension method, I suppose no explanations needed on that.

I have not played with this method much, but it seems that this will allow you to access the very same thing as the JS trick: the cookieset for the current URL. It probably will not allow to set anything, nor to peek cookies for other URLs. Maybe if you play hard with the CookieContainer you will receive, but I doubt.

On the 7.0 release, I've been struggling quite hard to achieve "cookie transparency" for my App. Long story short, my app was doing some background HTTP requests, and also had a WebBrowser to show some online content -- and "it would be great" if both sources of connections would emit the same cookies to the server.. And guess what, my application had to make the first request, then let the browser navigate. With such requirements, there was virtually is no way to achieve consistency of the cookies - bah, even with the current new and glorious GetCookie method, I suppose it would be damn hard. So, to the point - it was possible, but needed to use some hidden API, that is present publicitly on the Phone, but is hidden in the SDK. The API is available the (public) class System.Net.Browser.WebRequestCreator, freely available. The quirk is: in the SDK this class has a single public static property "IWebRequestCreate ClientHttp" with a method "Create" that you can use to "factory" your "raw http" connections - in case you dont want to use the WebClient for some reason. On the phone, and on the emulator, there is a second public static property called "IWebRequestCreate BrowserHttp", easily returned by Reflection:

PropertyInfo brwhttp = typeof(System.Net.Browser.WebRequestCreator)
    .GetProperty("BrowserHttp")

with this property, you will be able to obtain a "special" internal instance of IWebRequestCreate that is used internally by the WebBrowser. By opening your background HTTP requests with this class, you will get your cookies automatically set as if they were created/sent by the WebBrowser control, but in turn - you will NOT be able to modify http headers, userprovide http user authentication and neither do a few lowlevel things - because all that settings will be synced with the WebBrowser's data stored for current 'system user instance', if I'm allowed to call it as such on the single-user Phone device heh. The interoperation between connections and the WebBrowser works both ways - if your HTTP connection (created with use of the 'hidden property') receives any settings/cookies/etc -- then the WebBrowser will instantly notice them and update its own cache. No cookie/session loss on neither of the sides!

If you need to passively get cookies for your subsequent connections after some first WebBrowser navigation - please use the GetCookie or the JS way. But if you need your code to be first, and then pass authz to the WebBrowser -- you will probably have to dig deeper and use the above.. It's been hidden, so please resort to the other means first!

..and don't ask me how did I found it or how much time it took :P have a nice fun with it

//edit: I've just found out, that the BrowserHttp property is a normal Silverlight's way to access the Browser's connection factory, please see BrowserHttp. It seems that it is only has been hidden in the 'miniSilverlight' written for the WP7 platform!



回答2:

The approach being described in the post you linked is to use the WebBrowser control's InvokeScript method to run some javascript. However the post appears to use a "cookies" collection which doesn't actually exist.

 string cookie = myWebBrowser.InvokeScript("document.cookie") as string;

Now for the hard part the string you get contains all pertinent cookie name/value pairs for the page with the values still being Url encoded. You will need to parse the returned string for the value you need.

See document.cookie property documentation.

Edit:

Looking at it fresh instead of relying on the post, InvokeScript invokes named function on the window of the host browser. Hence the page being displayed in the WebBrowser would itself need to include a function like:-

 function getCookie() { return document.cookie; }

Then the InvokeScript would look like:-

 string cookie = myWebBrowser.InvokeScript("getCookie");


回答3:

As @quetzalcoatl already suggested, you can use internal instance of WebRequestCreator to share cookies between browser instances and instances of WebRequest. You don't get to access the cookies directly though, I think that's just a security measure by Microsoft.

This code below creates a WebReqeust object, connected to CookieContainer of WebBrowser instance. It then posts to a url to log in the user and store cookies in the container. After it's done, all browser instances within the app instance will have required set of cookies.

var browser = new WebBrowser();
var brwhttp = typeof (WebRequestCreator).GetProperty("BrowserHttp");
var requestFactory = brwhttp.GetValue(browser, null) as IWebRequestCreate;
var uri = new Uri("https://www.login.com/login-handler");

var req = requestFactory.Create(uri);
req.Method = "POST";

var postParams = new Dictionary<string, string> { 
    {"username", "turtlepower"}, 
    {"password": "ZoMgPaSSw0Rd1"}
};

req.BeginGetRequestStream(aReq => {

    var webRequest = (HttpWebRequest)aReq.AsyncState;
    using (var postStream = webRequest.EndGetRequestStream(aReq)) {

        // Build your POST request here
        var postDataBuilder = new StringBuilder();
        foreach (var pair in paramsDict) {
            if (postDataBuilder.Length != 0) {
                postDataBuilder.Append("&");
            }
            postDataBuilder.AppendFormat("{0}={1}", pair.Key, HttpUtility.UrlEncode(pair.Value));
        }

        var bytes = Encoding.UTF8.GetBytes(postDataBuilder.ToString());
        postStream.Write(bytes, 0, bytes.Length);
    }

    // Receive response 
    webRequest.BeginGetResponse(aResp => {

        var webRequest2 = (HttpWebRequest) aResp.AsyncState;

        webRequest = (HttpWebRequest)aResp.AsyncState;
        string resp;

        using (var response = (HttpWebResponse)webRequest2.EndGetResponse(aResp)) {
            using (var streamResponse = response.GetResponseStream()) {
                using (var streamReader = new System.IO.StreamReader(streamResponse)) {
                    resp = streamReader.ReadToEnd();
                }
            }
        }

    }, webRequest);
}, req);

One of the issues I couldn't solve though was exceptions thrown when server returns 302 - it seems to throw WebException error with "Not found" description.



回答4:

// Ensure this is set to true BEFORE navigating to the page
webBrowser1.IsScriptEnabled = true;

// Once the page has loaded, you can read the cookie string
string cookieString = webBrowser1.InvokeScript("eval", new string[] { "document.cookie;" }) as string;

The cookieString variable will contain the full cookie for the document. You can then parse the string.



回答5:

There is an WebBrowser Extension class which is exactly developed for this:

CookieCollection tempCookies = Microsoft.Phone.Controls.WebBrowserExtensions.GetCookies(this.BrowserControl);