Well, after much moaning and complaining and cursing of Bill Gates, I've decided that I need to update the C# attempt. Turns out, there's an easy way in .NET, and there's the horribly hard way.
The easy way is to work with the library. The hard way is to to brute force it. Guess what my category my last attempt fell under.
But let me start by saying that I probably would've left it at that if not for the patient guidance of these gentlemen - linkdown, porges, and a fellow by the nick of Kanibiss. So thanks guys.
So, a dissection of where I went wrong... well basically, it was the dismissal of System.Net.WebClient as being "too high level"... ah, hubris, my eternal enemy. Now I knew that to collect cookies I'd need to add a CookieContainer to an HttpWebRequest; my error was in assuming that WebClient didn't expose this in any way that I could use.
WebClient contained all the functionality I had replicated in my HttpWebRequestWrapper - URI encoding, setting of headers, etc, butI just couldn't figure out how to set a CookieContainer and without doing so, cookies aren't retained.
I had actually seen the method WebClient.GetWebRequest in the documentation, but had dismissed it - after all, it only returned a WebRequest object, it didn't return a reference to the WebRequest object being used by WebClient. The documentation contained a code sample showing exactly what I needed to do, but my inexperience tripped me up here and I totally missed the import of it.
After linkdown kept prompting me to investigate further, I downloaded Reflector and had a closer look at WebClient. Specifically, the UploadValues method, as this was the one I would like to have used... and lo and behold, the way was revealed.
The relevant bit is the call to... GetWebRequest. Ah. It was then that I noted that GetWebRequest was designated as virtual - that is, as overrideable by subclasses of WebClient.WebRequest request = null;this.ClearWebClientState();try{byte[] buffer = this.UploadValuesInternal(data);this.m_Method = method;request = this.m_WebRequest = this.GetWebRequest(this.GetUri(address));this.UploadBits(request, null, buffer, null, null, null, null);byte[] buffer2 = this.DownloadBits(request, null, null, null);if (Logging.On){Logging.Exit(Logging.Web, this, "UploadValues", address + ", " + method);}buffer3 = buffer2;}
A minor epiphany occurred - the designers of this class had left me a hook, as it were, but I was too inexperienced to see it. I felt very stupid and elated at the same time.
I quickly sat down and hacked out some code to use it, and then deleted it and rewrote it to allow for generalised usage - my first attempt looked like this -
1 using System;2 using System.Collections.Generic;3 using System.Text;4 using System.Net;567 namespace firstCookie28 {9 public delegate void ModifyRequest(WebRequest w);1011121314 class CustomWebClient : WebClient15 {16 Queue<modifyrequest> DelegateQueue;1718 public CustomWebClient() : base()19 {20 DelegateQueue= new Queue (); 21 }2223 protected override WebRequest GetWebRequest(Uri address)24 {25 WebRequest request = base.GetWebRequest(address);26 ApplyDelegates(request);27 return request;28 }2930 private WebRequest ApplyDelegates(WebRequest request)31 {32 while (DelegateQueue.Count > 0)33 {34 ModifyRequest modDelegate = DelegateQueue.Dequeue();35 modDelegate(request);36 }37 return request;38 }39 public void AddDelegate(ModifyRequest del)40 {41 DelegateQueue.Enqueue(del);42 }4344 }45 }
After some sage advice from this fine fellow, I modified the class to use events:
26 class CustomWebClient : WebClient27 {28 public event Action<WebRequest> ModifyRequest;2930 public CustomWebClient() : base() { }3132 protected override WebRequest GetWebRequest(Uri address)33 {34 WebRequest request = base.GetWebRequest(address);35 OnModifyRequest(request);36 return request;37 }3839 private void OnModifyRequest(WebRequest request)40 {41 if (ModifyRequest != null)42 ModifyRequest(request);43 }4445 }
Which can then be used like so:
static void Main(string[] args){CustomWebClient w = new CustomWebClient();CookieContainer cj = new CookieContainer();w.ModifyRequest += delegate(WebRequest request){((HttpWebRequest)request).CookieContainer = cj;};w.ModifyRequest += delegate(WebRequest request){((HttpWebRequest)request).AllowAutoRedirect = true;};string v = w.DownloadString("http://www.google.co.nz");}
So. Now I get to rewrite the rest of my C# piece. I'm very embarrassed at my mistake, but I learnt a lot about .NET in the process. I also have a far better vehicle for my HTTP requests now, far more flexible and far more powerful. My thanks to all who gave advice. :)
Click here for Part 3.3.2
0 comments:
Post a Comment