I've previously used a CookieContainer with HttpWebRequest and HttpWebResponse sessions, but now, I want to use it with a WebClient. As far as I understand, there is no built-in method like there is for HttpWebRequests (request.CookieContainer). How can I collect cookies from a WebClient in a CookieContainer?
I googled for this and found the following sample:
public class CookieAwareWebClient : WebClient
{
private readonly CookieContainer m_container = new CookieContainer();
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
HttpWebRequest webRequest = request as HttpWebRequest;
if (webRequest != null)
{
webRequest.CookieContainer = m_container;
}
return request;
}
}
Is this the best way to do it?
WebClient wb = new WebClient();
wb.Headers.Add(HttpRequestHeader.Cookie, "somecookie");
From Comments
How do you format the name and value of the cookie in place of "somecookie" ?
wb.Headers.Add(HttpRequestHeader.Cookie, "cookiename=cookievalue");
For multiple cookies:
wb.Headers.Add(HttpRequestHeader.Cookie,
"cookiename1=cookievalue1;" +
"cookiename2=cookievalue2");
Yes. IMHO, overriding GetWebRequest() is the best solution to WebClient's limited functionalty. Before I knew about this option, I wrote lots of really painful code at the HttpWebRequest layer because WebClient almost, but not quite, did what I needed. Derivation is much easier.
Another option is to use the regular WebClient class, but manually populate the Cookie header before making the request and then pull out the Set-Cookies header on the response. There are helper methods on the CookieContainer class which make creating and parsing these headers easier: CookieContainer.SetCookies() and CookieContainer.GetCookieHeader(), respectively.
I prefer the former approach since it's easier for the caller and requires less repetitive code than the second option. Also, the derivation approach works the same way for multiple extensibility scenarios (e.g. cookies, proxies, etc.).
This one is just extension of article you found.
public class WebClientEx : WebClient
{
public WebClientEx(CookieContainer container)
{
this.container = container;
}
public CookieContainer CookieContainer
{
get { return container; }
set { container= value; }
}
private CookieContainer container = new CookieContainer();
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest r = base.GetWebRequest(address);
var request = r as HttpWebRequest;
if (request != null)
{
request.CookieContainer = container;
}
return r;
}
protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
{
WebResponse response = base.GetWebResponse(request, result);
ReadCookies(response);
return response;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
ReadCookies(response);
return response;
}
private void ReadCookies(WebResponse r)
{
var response = r as HttpWebResponse;
if (response != null)
{
CookieCollection cookies = response.Cookies;
container.Add(cookies);
}
}
}
The HttpWebRequest modifies the CookieContainer assigned to it. There is no need to process returned cookies. Simply assign your cookie container to every web request.
public class CookieAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; set; } = new CookieContainer();
protected override WebRequest GetWebRequest(Uri uri)
{
WebRequest request = base.GetWebRequest(uri);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = CookieContainer;
}
return request;
}
}
I think there's cleaner way where you don't have to create a new webclient (and it'll work with 3rd party libraries as well)
internal static class MyWebRequestCreator
{
private static IWebRequestCreate myCreator;
public static IWebRequestCreate MyHttp
{
get
{
if (myCreator == null)
{
myCreator = new MyHttpRequestCreator();
}
return myCreator;
}
}
private class MyHttpRequestCreator : IWebRequestCreate
{
public WebRequest Create(Uri uri)
{
var req = System.Net.WebRequest.CreateHttp(uri);
req.CookieContainer = new CookieContainer();
return req;
}
}
}
Now all you have to do is opt in for which domains you want to use this:
WebRequest.RegisterPrefix("http://example.com/", MyWebRequestCreator.MyHttp);
That means ANY webrequest that goes to example.com will now use your custom webrequest creator, including the standard webclient. This approach means you don't have to touch all you code. You just call the register prefix once and be done with it.
You can also register for "http" prefix to opt in for everything everywhere.
I'm trying to add an event to Google Calendar from within a test console app.
I have a token_refresh obtained for the scope "https://www.googleapis.com/auth/calendar" via a web application that I registered at the Google API Console.
I use this stored token_refresh in my test console app to get an access_token.
Then I want to add an event to the calendar of the user that corresponds to the refresh_token that I have.
So I build a POST HttpWebRequest as follows:
HttpWebRequest request = WebRequest.Create("https://www.googleapis.com/calendar/v3/calendars/" + calendarID + "/events") as HttpWebRequest; // Where calendarID is like joe#gmail.com
request.Headers.Add("Authorization", "Bearer " + access_token);
request.Headers.Add("Accept-Charset", "UTF-8");
request.Headers.Add("ContentType", "application/json");
request.ProtocolVersion = HttpVersion.Version11;
request.KeepAlive = false;
request.Method = "POST";
GoogleCalendarEvent anEvent = new GoogleCalendarEvent();
anEvent.start = new GoogleCalendarEventTime(DateTime.Today);
anEvent.end = new GoogleCalendarEventTime(DateTime.Today);
string data = jsonSerializer.Serialize(anEvent); // jsonSerializer is a JavaScriptSerializer object
byte[] postData = Encoding.UTF8.GetBytes(data);
Stream ws = request.GetRequestStream();
ws.Write(postData, 0, postData.Length);
ws.Close();
WebResponse response = request.GetResponse(); // throws WebException The remote server returned an error: (400) Bad Request.
stream = new StreamReader(response.GetResponseStream());
string result = stream.ReadToEnd().Trim();
And the helper classes are:
private class GoogleCalendar
{
private string kind;
private string etag;
private string id;
private string summary;
private string timezone;
private string accesrole;
public string Kind
{
get { return this.kind;}
set { this.kind = value; }
}
public string Etag
{
get { return this.etag; }
set { this.etag = value; }
}
public string ID
{
get { return this.id; }
set { this.id = value; }
}
public string Summary
{
get { return this.summary; }
set { this.summary = value; }
}
public string TimeZone
{
get { return this.timezone; }
set { this.timezone = value; }
}
public string AccessRole
{
get { return this.accesrole; }
set { this.accesrole = value; }
}
}
private class GoogleCalendarEventTime
{
private DateTime _date;
public string date
{
get
{
return this._date.ToString("yyyy-mm-dd");
}
}
public GoogleCalendarEventTime(DateTime time)
{
this._date = time;
}
}
My problem is that when the I try to get a response for the request I get an Error 400 Bad request. This type of error code is for both when a POST is badly constructed and as a placeholder for unknown errors.
Google provides a client library for .NET, which will make this much easier for you. An explanation of how to configure the client library with the Calendar API is available here (click the .NET tab on that page).
This line constructs the service object which you will use to make API calls:
var service = new CalendarService();
Unfortunately, there aren't comprehensive samples for using .NET with all of the API methods of the Calendar API, but you can see a list of all the APIs supported by this client library, many of which have examples. The usage pattern will be nearly identical across the APIs.
I have a situation when the source I get from a site using WebClient/HttpWebRequest is different from the actual source from web browsing (different button_onclick).
I guess the site updates the button very short after sending the first source,
I would like to get an updated source of a site after like 1 second I've "been" there.
I've tried something, but I don't really know how to do that.
This is my try:
public class KeepAliveWebClient : WebClient
{
public string DownloadString(string address)
{
return base.DownloadString(address);
}
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.KeepAlive = true;
return request;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
Thread.Sleep(2000);
var newResponse = base.GetWebResponse(request);
return newResponse;
}
}
Thanks in advance.
Is it possible on the WebClient class?
E.g. something like:
MyWebClient.AllowAutoRedirect = false; (of HttpWebRequest)
You could write a custom web client and enable this functionality:
public class WebClientEx : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.AllowAutoRedirect = false;
return request;
}
}
and then:
using (var client = new WebClientEx())
{
Console.WriteLine(client.DownloadString("http://google.com"));
}
Im using code like this to check if URL's are real and exist:
its working fine generally, but it's not working for youtube urls..
eg this valid url: http://www.youtube.com/watch?v=c4IBN5OAdZc
Utils.UrlExists(uri)
public static bool UrlExists(string url)
{
using (var client = new MyClient())
{
client.HeadOnly = true;
// fine, no content downloaded
try
{
string s1 = client.DownloadString(url);
return true;
}
catch { return false; }
}
}
class MyClient : WebClient
{
public bool HeadOnly { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest req = base.GetWebRequest(address);
if (HeadOnly && req.Method == "GET")
{
req.Method = "HEAD";
}
return req;
}
}
Consider this approach:
Hit this URL with your C# code, and analyze the response:
http://gdata.youtube.com/feeds/api/videos/<your ID>
You will be able to see the HTTP response of 200 when successful.
Try it in your browser; you'll see that good IDs give you content, others will return plaintext of "Private video" or "Invalid id".
erics comment was the answer. If he puts an answer I'll mark it as correct. in the meantime I'll mark this as correct