I am trying to pass login creds from a WebView into an HttpWebRequest but not having any luck getting an authenticated response. I am able to successfully make the request, but the response is acting like I haven't logged in. My app has 5 WebViews contained within Fragment s and I'm logged in on all of them. I've tried using the CookieSyncManager but it's deprecated and .Sync() didn't work. I've tried a lot of different ways of passing the cookies into the HttpRequest with no success and many hours spent.
One would think this is a simple request; user has logged in within the app; they should be authenticated for all requests. Here's the closest that I've gotten, but the response string is still not the same as through my authenticated WebView :
This attempt parses each Cookie into a string and adds it
public string _cookieString { get; set; }
private class ExtWebViewClient : WebViewClient
{
TheFragment5 _fm5 = new TheFragment5();
public override void OnPageFinished(WebView view, string url)
{
var cookieHeader = Android.Webkit.CookieManager.Instance.GetCookie(url);
var cookiePairs = cookieHeader.Split('&');
_fm5._cookieString = "";
foreach (var cookiePair in cookiePairs)
{
var cookiePieces = cookiePair.Split('=');
if (cookiePieces[0].Contains(":"))
cookiePieces[0] = cookiePieces[0].Substring(0, cookiePieces[0].IndexOf(":"));
cookies.Add(new Cookie
{
Name = cookiePieces[0],
Value = cookiePieces[1]
});
}
foreach (Cookie c in cookies)
{
if (_fm5._cookieString == "")
{
_fm5._cookieString = c.ToString();
}
else
{
_fm5._cookieString += c.ToString();
}
}
}
}
I've also tried just doing:
_fm5._cookieString = cookieHeader.ToString();
but neither of those attempts is working when I add the cookie string into my HttpRequest :
public async void GetNotificationText(string url)
{
//var _cmhc = _cookieMan.HasCookies;
await Task.Run(() =>
{
_notificationHttpRequestInProgress = true;
try
{
var _ctxxx = Android.App.Application.Context;
//URL _url2 = new URL("https://bitchute.com/notifications/");
//HttpURLConnection conn = (HttpURLConnection)_url2.OpenConnection();
//conn.ReadTimeout = 10000 /* milliseconds */;
//conn.ConnectTimeout = 15000 /* milliseconds */;
////conn.SetRequestProperty("Cookie", cookies);
//conn.Connect();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
Uri uri = new Uri(url);
var _req = request;
var _uriii = uri;
var _cookiesss = _fm5._cookieString;
_cookieCon.SetCookies(uri, _cookiesss);
request.CookieContainer = _cookieCon;
//request.CookieContainer.SetCookies(uri, _cookiesss);
request.AutomaticDecompression = DecompressionMethods.GZip;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
_notificationRawText = reader.ReadToEnd();
Console.WriteLine(_notificationRawText);
_rawNoteText = _notificationRawText;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
_notificationHttpRequestInProgress = false;
});
}
This returns, but not the authenticated webtext request; I get the same response any user would get on a browser having never logged in. If I were to browse out to this same url on any WebView in my app, I'd get a completely different response.
You will also notice some commented out code that was another failed attempt at adding the cookies into a connection. I had also tried using HttpURLConnection.SetRequestProperty("Cookie", cookies);
where cookies was a CookieCollection and that didn't work either. The code is mostly commented out and layered because I've been trying this for days.
Does anyone know how I can pass WebView cookies into an HttpRequest using Xamarin.Android?
I am putting this code below in Fragment5 of my app; you can see and compile the full context here:
https://github.com/hexag0d/BitChute_Mobile_Android_BottomNav/blob/NotificationAdder/Fragments/TheFragment5.cs
I'm not sure exactly why the above example didn't work; maybe if you're better at .NET than I am, you could figure it out. However, I was able to successfully pass WebView creds into an HttpClient by following these steps, which are returning an authenticated response. This may not be the most elegant way of doing it, but you can always refine my answer, or post a better one.
What I had to do was set the HttpClient.DefaultRequestHeaders using the .Add() method like this: _client.DefaultRequestHeaders.Add("Cookie", TheFragment5._cookieHeader);
I got the CookieHeader (which is just a string btw) like this:
//instantiate a string that will house our cookie header
public static string _cookieHeader;
//you might want to make it private to prevent abuse
//but this example is just for demonstration
//the thing is we need a string to house our headers in scope of both the WebView and the HttpClient
//extend the WebViewClient
private class ExtWebViewClient : WebViewClient
{
public override void OnPageFinished(WebView view, string url)
{
//I get the cookies when the page finishes loading because
//then we know the cookie has our login cred header
//also, most of the previous examples got the cookies OnPageFinished
TheFragment5._cookieHeader = Android.Webkit.CookieManager.Instance.GetCookie(url);
}
}
Then we need another method for the HttpClient and HttpClientHandler ... mine scans a webpage for notification text.
public async void GetNotificationText(string url)
{
await Task.Run(() =>
{
/* this line is pretty important,
we need to instantiate an HttpClientHandler
then set it's UseCookies property to false
so that it doesn't override our cookies
*/
HttpClientHandler handler = new HttpClientHandler() { UseCookies = false };
try
{
Uri _notificationURI = new Uri("https://bitchute.com/notifications/");
//instantiate HttpClient using the handler
using (HttpClient _client = new HttpClient(handler))
{
//this line is where the magic happens;
//we set the DefaultRequestHeaders with the cookieheader we got from WebViewClient.OnPageFinished
_client.DefaultRequestHeaders.Add("Cookie", TheFragment5._cookieHeader);
//do a GetAsync request with our cookied up client
var getRequest = _client.GetAsync("https://bitchute.com/notifications/").Result;
//resultContent is the authenticated html string response from the server, ready to be parsed =]
var resultContent = getRequest.Content.ReadAsStringAsync().Result;
/*
I was writing to console to check the
response.. for me, I am now getting
the authenticated notification html
page
*/
Console.WriteLine(resultContent);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Hope this helps you, posting for future reference, especially for people using Xamarin.Android.
hello I am trying to launch an intent with a webview from a user entered URL, I have been looking everywhere online and I can't find a concrete answer as to how to make sure the website will actually connect before allowing the user to proceed to the next activity. I have found many tools to make sure the URL follows the correct format but none that actually let me make sure it can actually connect.
You can use WebClient and check if any exception is thrown:
using (var client = new HeadOnlyClient())
{
try
{
client.DownloadString("http://google.com");
}
catch (Exception ex)
{
// URL is not accessible.
}
}
You can catch more specific exceptions to make it more elegant.
You can also use custom modification to WebClient to check HEAD only and decrease the amount of data downloaded:
class HeadOnlyClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest req = base.GetWebRequest(address);
req.Method = "HEAD";
return req;
}
}
I would suggest you to use HttpHead for simple request with AndroidHttpClient, but it is deprecated now. You can try to implement HEAD Request by sockets.
You can try to ping the address first.
See this SO question: How to Ping External IP from Java Android
Another option:
Connectivity Plugin for Xamarin and Windows
Task<bool> IsReachable(string host, int msTimeout = 5000);
But, any pre-check that succeeds isn't guaranteed as the very next request might fail so you should still handle that.
Here's what I ended up doing to Check if a Host name is reachable. I was connecting to a site with a self signed certificate so that's why I have the delegate in the ServiceCertificateValidationCallback.
private async Task<bool> CheckHostConnectionAsync (string serverName)
{
string Message = string.Empty;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serverName);
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true;
};
// Set the credentials to the current user account
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
request.Method = "GET";
request.Timeout = 1000 * 40;
try
{
using (HttpWebResponse response = (HttpWebResponse) await request.GetResponseAsync ())
{
// Do nothing; we're only testing to see if we can get the response
}
}
catch (WebException ex)
{
Message += ((Message.Length > 0) ? "\n" : "") + ex.Message;
return false;
}
if (Message.Length == 0)
{
goToMainActivity (serverName);
}
return true;
}
i'm new to wp8 and i don't have any idea to post and update my data in URL so please can any one help me to post/Update data in the url..
my header and content type...
Header - Accept: application/json
Content-Type application/json
MY Class Register with the variables...
public class Register
{
public string email { get; set; }
public string password { get; set; }
}
My Design..
when i click ADD button it should post the data in my url.. so plz help me to do it..
MY CODE for post method..
WebClient client = new WebClient();
Register res = new Register();
res.email = txt_email.Text;
res.password = txt_password.Text;
String json = JsonConvert.SerializeObject(res);
client.Headers[HttpRequestHeader.Accept] = "application/json";
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.UploadStringCompleted += (object source, UploadStringCompletedEventArgs ex) =>
{
if (ex.Error != null || ex.Cancelled)
{
// Error or cancelled
MessageBox.Show(ex.Result);
}
};
var uri = new Uri(url, UriKind.Absolute);
// client.Encoding = System.Text.Encoding.UTF8;
client.UploadStringAsync(uri,json); // message is the json content in string
ERROR.. on Adding content..
I use RestSharp in my projects for that kind of thing. It simplifies things a bit particularly if you want to access cookies.
There's some examples on their wiki that should show you what to do.
http://restsharp.org/
You can use WebClient
public void PostJSON()
{
client = new WebClient();
client.Headers[HttpRequestHeader.Accept] = "application/json";
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.UploadStringCompleted += (object source, UploadStringCompletedEventArgs e) =>
{
if (e.Error != null || e.Cancelled)
{
// Error or cancelled
}
};
client.UploadStringAsync(url, message); // message is the json content in string
}
Your code seems legit to me. Since you get the error at runtime, I doubt that you might not have a proper Internet connection in your emulator. Or, if you are running your web service from localhost, you must help your emulator reach your local service.
Case #1: No proper Internet connection
Refer the following link and troubleshoot your network problem as stated in the MSDN page.
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj681694(v=vs.105).aspx
Case #2: Help emulator to connect to a local web service
Even when you can connect to external web content, you still need to configure your emulator to access your local web service. Go through the MSDN article given at the link below.
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj684580(v=vs.105).aspx
Feel free to leave a comment if you need further assistance.
I am facing some problems when using the HttpClient class to access to a Delicious API. I have the following code:
try
{
const string uriSources = "https://api.del.icio.us/v1/tags/bundles/all?private={myKey}";
using (var handler = new HttpClientHandler { Credentials = new
NetworkCredential("MyUSER", "MyPASS") })
{
using (var client = new HttpClient(handler))
{
var result = await client.GetStringAsync(uriSources);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "ERROR...", MessageBoxButton.OK);
}
When running the code above I am getting the following: Response status code does not indicate success: 401 (Unauthorized).
So, how could I get this work? Is it possible?
Thanks in advance
Regards!
I had the exact same problem myself. It seems the HttpClient just disregards the credentials set in the HttpClientHandler.
The following shall work however:
using System.Net.Http.Headers; // For AuthenticationHeaderValue
const string uri = "https://example.com/path?params=1";
using (var client = new HttpClient()) {
var byteArray = Encoding.ASCII.GetBytes("MyUSER:MyPASS");
var header = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(byteArray));
client.DefaultRequestHeaders.Authorization = header;
var result = await client.GetStringAsync(uri);
}
No need for the handler.
Source: http://www.snip2code.com/Snippet/13895/Simple-C---NET-4-5-HTTPClient-Request-Us
This is an old post but thought to add my reply for someone facing similar issue and browsing answers...
I faced similar issue. In my case, setting Domain property for NetworkCredentials worked. You can try setting Domain.
The code you are showing works for me against an authenticated resource. I suspect Delicious is doing something weird.
Considering you are on Windows Phone, it is a pain to debug with Fiddler, so what I suggest is getting a Runscope account. Install this message handler which will redirect your request via the RunScope debugger. Once you do this, I suggest you look at the www-authenticate header and examine what that is returning.
If all else fails you can always set the Authentication header directly with basic auth credentials. You don't need to use the Credentials class.
I need to create a request for a web page delivered to our web sites, but I need to be able to set the host header information too. I have tried this using HttpWebRequest, but the Header information is read only (Or at least the Host part of it is). I need to do this because we want to perform the initial request for a page before the user can. We have 10 web server which are load balanced, so we need to request the file from each of the web servers.
I have tried the following:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Headers.Set("Host", "www.mywebsite.com");
WebResponse response = request.GetResponse();
Obviously this does not work, as I can't update the header, and I don't know if this is indeed the right way to do it.
Although this is a very late answer, maybe someone can get benefit of it
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://192.168.1.1"));
request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, request.Headers, new object[] {"Host","www.mysite.com"});
Reflection is your friend :)
I have managed to find out a more long winded route by using sockets. I found the answer in the MSDN page for IPEndPoint:
string getString = "GET /path/mypage.htm HTTP/1.1\r\nHost: www.mysite.mobi\r\nConnection: Close\r\n\r\n";
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
Socket socket = null;
String strPage = null;
try
{
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.23.1.93"), 80);
socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip);
}
catch (SocketException ex)
{
Console.WriteLine("Source:" + ex.Source);
Console.WriteLine("Message:" + ex.Message);
}
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
while (bytes > 0)
{
bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();
I had a problem where the URL dns I used had several different IP addresses, I wanted to call each address separately using the same dns name in the host - the solution is using a proxy:
string retVal = "";
// Can't change the 'Host' header property because .NET protects it
// HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
// request.Headers.Set(HttpRequestHeader.Host, DEPLOYER_HOST);
// so we must use a workaround
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Proxy = new WebProxy(ip);
using (WebResponse response = request.GetResponse())
{
using (TextReader reader = new StreamReader(response.GetResponseStream()))
{
string line;
while ((line = reader.ReadLine()) != null)
retVal += line;
}
}
return retVal;
Host header is set from 'url' automatically by .NET, and 'ip' contains the actual address of the web server you want to contact (you can use a dns name here too)
I know this is old, but I came across this same exact problem, and I found a better solution to this then using sockets or reflection...
What I did was create a new class that durives from WebHeaderCollection and bypasses validation of what you stick inside it:
public class MyHeaderCollection:WebHeaderCollection
{
public new void Set(string name, string value)
{
AddWithoutValidate(name, value);
}
//or
public new string this[string name]
{
get { return base[name]; }
set { AddWithoutValidate(name, value); }
}
}
and here is how you use it:
var http = WebRequest.Create("http://example.com/");
var headers = new MyHeaderCollection();
http.Headers = headers;
//Now you can add/override anything you like without validation:
headers.Set("Host", http.RequestUri.Host);
//or
headers["Host"] = http.RequestUri.Host;
Hope this helps anyone looking for this!
I know this is an old question, but these days, you can do.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Host = "www.mywebstite.com";
WebResponse response = request.GetResponse();
The "Host" header is protected and cannot be modified programmatically. I suppose to work around this, you could try and bind via reflection to the private "InnerCollection" property of the WebRequest object and calling the "Set" ar "Add" method on it to modify the Host header. I haven't tried this, but from a quick look at the source code in Reflector, I think it's easily accomplished. But yeah, binding to private properties of framework objects is not the best way to accomplish things. :) Use only if you MUST.
edit: Or like the other guy mentions in the linked question, just open up a socket and do a quick "GET" manually. Should be a no brainer, if you don't need to tinker with other stuff, like cookies or whatever else niceties the HttpWebRequest provides.
Alright, little bit of research turns up this:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456
Seems MS may do something about this at some point.
You can use my solution for this problem, it posted here :
How to set custom "Host" header in HttpWebRequest?
This can help you to edit host header, and avoid to using proxy and direct socket requests.
Necromancing.
For those still on .NET 2.0
It is in fact quite easy, if you know how.
Problem is, you can't set the host header, because the framework won't let you change the value at runtime. (.net framework 4.0+ will let you override host in a httpwebrequest).
Next attempt will be setting the header with reflection - as demonstrated in the top upvoted answer here - to get around it, which will let you change the header value. But at runtime, it will overwrite this value with the host part of the url, which means reflection will bring you nothing, which is why I don't understand why people keep upvoting this.
If the dns-name doesn't exist, which is quite frankly the only case in which you want to do this in the first place, you can't set it, because .NET can't resolve it, and you can't override the .NET DNS resolver.
But what you can do, is setting a webproxy with the exact same IP as the destination server.
So, if your server IP is 28.14.88.71:
public class myweb : System.Net.WebClient
{
protected override System.Net.WebRequest GetWebRequest(System.Uri address)
{
System.Net.WebRequest request = (System.Net.WebRequest)base.GetWebRequest(address);
//string host = "redmine.nonexistantdomain.com";
//request.Headers.GetType().InvokeMember("ChangeInternal",
// System.Reflection.BindingFlags.NonPublic |
// System.Reflection.BindingFlags.Instance |
// System.Reflection.BindingFlags.InvokeMethod, null,
// request.Headers, new object[] { "Host", host }
//);
//server IP and port
request.Proxy = new System.Net.WebProxy("http://28.14.88.71:80");
// .NET 4.0 only
System.Net.HttpWebRequest foo = (System.Net.HttpWebRequest)request;
//foo.Host = host;
// The below reflection-based operation is not necessary,
// if the server speaks HTTP 1.1 correctly
// and the firewall doesn't interfere
// https://yoursunny.com/t/2009/HttpWebRequest-IP/
System.Reflection.FieldInfo horribleProxyServicePoint = (typeof(System.Net.ServicePoint))
.GetField("m_ProxyServicePoint", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
horribleProxyServicePoint.SetValue(foo.ServicePoint, false);
return foo; // or return request; if you don't neet this
}
}
and voila, now
myweb wc = new myweb();
string str = wc.DownloadString("http://redmine.netexistantdomain.com");
and you get the correct page back, if 28.14.88.71 is a webserver with virtual name-based hosting (based on http-host-header).
Now you have the correct answer to the original question, for both WebRequest and WebClient. I think using custom sockets to do this would be the wrong approach, particularly when SSL should be used, and when an actual solution is that simple...