I'm trying to write a client for either HTTP Post or HTTP Get using HttpClient. When Googling around I come across these methods that set these authentication within the HttpClient object. One uses NetworkCredential while the other uses AuthenticationHeaderValue
HttpClient sClient;
HttpClientHandler sHandler = new HttpClientHandler();
sHandler.Credentials = new NetworkCredential("UserName", "Password");
sClient = new HttpClient(sHandler);
OR
HttpClient sClient new HttpClient();
sClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("UserName:Password")));
Reading on MSDN does not give me a distinct answer about the differences between them. Is this a case where both will do the same thing except how its authentication information is stored? such as AuthenticationHeaderValue puts it in the header while the other doesn't? Is one better than the other in term of my use case or best practices ?
The 2nd approach is more flexible in such way that you can specify a type of authentication (e.g., anonymous, basic, window, certificate, etc) to use.
If your first approach doesn't work, try to specify the 3rd param on NetworkCredential, which is the domain name.
Related
I'm having difficulty understanding how web requests and credentials work in .NET.
I have the following method that is executing a request to a SOAP endpoint.
public WebResponse Execute(NetworkCredential Credentials)
{
HttpWebRequest webRequest = CreateWebRequest(_url, actionUrl);
webRequest.AllowAutoRedirect = true;
webRequest.PreAuthenticate = true;
webRequest.Credentials = Credentials;
// Add headers and content into the requestStream
asyncResult.AsyncWaitHandle.WaitOne();
return webRequest.EndGetResponse(asyncResult);
}
It works well enough. However, users of my applications may have to execute dozens of these requests in short succession. Hundreds over the course of the day. My goal is to implement some of the recommendations I've read about, namely using an HttpClient that exists for the entire lifetime of the application, and to use the CredentialCache to store user's credentials, instead of passing them in to each request.
So I'm starting with the CredentialCache.
Following the example linked above, I instantiated a CredentialCache and added my network credentials to it. Note that this is the exact same NetworkCredential object that I was passing to the request earlier.
NetworkCredential credential = new NetworkCredential();
credential.UserName = Name;
credential.Password = PW;
Program.CredCache.Add(new Uri("https://blah.com/"), "Basic", credential);
Then, when I go to send my HTTP request, I get the credentials from the cache, instead of providing the credentials object directly.
public WebResponse Execute(NetworkCredential Credentials)
{
HttpWebRequest webRequest = CreateWebRequest(_url, actionUrl);
webRequest.AllowAutoRedirect = true;
webRequest.PreAuthenticate = true;
webRequest.Credentials = Program.CredCache;
// more stuff down here
}
The request now fails with a 401 error.
I am failing to understand this on several levels. For starters, I can't seem to figure out whether or not the CredentialCache has indeed passed the proper credentials to the HTTP request.
I suspect part of the problem might be that I'm trying to use "Basic" authentication. I tried "Digest" as well just as a shot in the dark (which also failed), but I'm sure there must be a way to see what kind of authentication the server is expecting.
I have been combing StackOverflow and MDN trying to read up as much as possible about this, but I am having a difficult time separating the relevant information from the outdated and irrelevant information.
If anyone can help me solve the problem that would be most appreciated, but even links to proper educational resources would be helpful.
According to the documentation the CredentialCache class is only for SMTP, it explicitly says that it is not for HTTP or FTP requests:
https://msdn.microsoft.com/en-us/library/system.net.credentialcache(v=vs.110).aspx
Which directly contradicts the info in the later api docs. Which one is right I don't know.
You could try using the HttpClient class. The methods and return types are different, so you would need to tweak your other a code a bit, but it would look a bit like this:
public class CommsClass
{
private HttpClient _httpClient;
public CommsClass(NetworkCredential credentials)
{
var handler = new HttpClientHandler { Credentials = credentials };
_httpclient = new HttpClient(handler);
}
public HttpResponseMessage Execute(HttpRequestMessage message)
{
var response = _httpClient.SendAsync(message).Result;
return response;
}
}
You can do all sorts of other things with the handler, and the client like set request headers or set a base address.
I saw couple of questions asked about core 2.0 on how to make HttpClient to use default proxy configured on the system. But no where found right answer. Posting this question hoping someone who might have encountered this issue might have found the solution by now.
In .net framework versions I've used the following configuration in my web.config and it worked for me.
<system.net>
<defaultProxy useDefaultCredentials="true"></defaultProxy>
</system.net>
But in .net core 2.0 where I've make a web request to external api from my company's intranet my code is failing with 407, proxy authentication required.
After little bit of research I am of the opinion that it is not possible to make your HttpClient to use default proxy settings configured via WPAD in IE. Can someone correct my understanding here?
On this page of https://github.com/dotnet/corefx/issues/7037
It is said as follows :
"The default for HttpClientHandler.UseProxy property is true. And the default value of HttpClientHandler.Proxy is NULL which means to use the default proxy."
But I don't observe this behavior.
Update:
I am finally able to call external web api by specifying the proxy server address and then making the HttpClient call. Still wondering how to use default proxy setup in IE.
using (var handler = new HttpClientHandler {
Credentials = new System.Net.NetworkCredential(user, password, domain),
UseProxy = true,
Proxy = new System.Net.WebProxy(new Uri("http://xxxxxxx:8080"), true)
})
{
handler.Proxy.Credentials = new NetworkCredential("xxxx", "yyyyy", "cccccc");
using (var httpClient = new HttpClient(handler))
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(destinationUrl),
Method = HttpMethod.Post
};
request.Content = new StringContent(requestXml, Encoding.UTF8, "text/xml");
HttpResponseMessage response = await httpClient.SendAsync(request);
Task<Stream> streamTask = response.Content.ReadAsStreamAsync();
}
}
If any one interested in finding out how I was able to find out the proxy server was, I wrote the following code in .net 4.0 and found out the proxy used.
var proxy = WebRequest.GetSystemWebProxy();
var url = proxy.GetProxy(new Uri("http://google.com"));
Thanks
I hope this is the answer you're looking for: Default Proxy issues #28780
If you simply want to use the default system proxy and need to pass default credentials to that proxy (because the proxy is an authenticated proxy) during HTTP requests, then do this:
var handler = new HttpClientHandler();
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
var client = new HttpClient(handler);
I am attempting to use DalSoft.RestClient to make restful calls to an internal service which requires network credentials (for my use case a default credential) be provided.
The constructor for RestClient provides an overload to pass in an IHttpClientWrapper which I could implement handling credentials, but am hoping there's an out of the box solution for passing credentials to RestClient.
How do I pass credentials to the DalSoft.RestClient?
For any credentials that are set via a header such as basic or oauth you can use the Headers methods. Example for oauth2 bearer token:
dynamic client = new RestClient("http://localhost/");
client
.Headers(new { Authorization = "Bearer " + bearerToken })
.MyResource
.Get();
If you are talking about kerberos or ntlm at the moment there is no method to do this but as you suggested you can implement IHttpClientWrapper to do this. Strangely Credentials are passed to a HttpClient using a HttpClientHandler. Below is an example of how to do this:
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential();
HttpClient client = new HttpClient(handler);
I realize implementing IHttpClientWrapper just to do this isn't ideal, so if you need this functionality I'll look at adding it to the ctor. It would look like this:
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential();
new RestClient("http://localhost/", new Config(handler));
Update this is now supported as of 3.0
What is the equivalent to WebResponse.IsFromCache when using HttpClient and HttpResponseMessage?
Is there some HTTP header in the response that I can look at?
FYI: The Windows.Web.Http HttpClient (a similar API targetted at Windows 8.1 app development) does include an HttpResponseMessage.Source field that specifies where the result came from (common values are "cache" and "network").
The Windows.Web.Http classes are usable from C# and other .NET languages, from C++, and from JavaScript (when running as a WwaHost app like from the Windows app store).
Can I ask what you're trying to achieve? Are trying to avoid caching?
The reason for asking is I've looked at the source code for HttpClient (specifically HttpClientHandler) and the source for HttpWebResponse and I dont believe you can get this information from the headers.
HttpClient/HttpClientHandler does use HttpWebResponse internally however it does not expose all properties from HttpWebResponse :
private HttpResponseMessage CreateResponseMessage(HttpWebResponse webResponse, HttpRequestMessage request)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(webResponse.StatusCode);
httpResponseMessage.ReasonPhrase = webResponse.StatusDescription;
httpResponseMessage.Version = webResponse.ProtocolVersion;
httpResponseMessage.RequestMessage = request;
httpResponseMessage.Content = (HttpContent) new StreamContent((Stream) new HttpClientHandler.WebExceptionWrapperStream(webResponse.GetResponseStream()));
//this line doesnt exist, would be nice
httpResponseMessage.IsFromCache = webResponse.IsFromCache;// <-- MISSING!
...
}
So your options the way I see it are:
a) Look at the source code for HttpWebRequest to determine the logic for IsFromCache and retrofit this somehow into HttpClient (this may not even be possible, depends on what the logic actually does/needs)
b)ask the ASP.NET team for this property to be included with HttpResponseMessage. either directly as a property or perhaps they could 'keep' the HttpWebResponse
Neither of these options are that great sorry, hence my original question, what are you trying to acheive?
I've been struggling with this scenario recently as well.
What I needed was an integration test to verify that:
Responses for a newly created resource had the correct headers set by the server.
Subsequent requests for that resource were fulfilled from the client-cache.
Responses for an existing resource had the correct headers set by the server as well.
What I ended up doing was a twofold check:
A non-caching HttpClient to check the initial response:
new WebRequestHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = new CookieContainer(),
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Refresh)
};
var client = new HttpClient(handler)
and a second HTTP client to check the client-side cache:
new WebRequestHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = new CookieContainer(),
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Default)
};
var client = new HttpClient(handler)
To verify the source of response messages I compare the HttpResponseMessage.Headers.Date values from steps 1 and 2 (which will be the same if the response came from the client cache). For my third step I can just re-use the client from the first step and append an arbitrary string to the URL.
Disclaimer: this applies to .NET Framework 4.7 and ignores best practices concerning HttpClient usage but is seems to do the trick for me in my test suite. An explicit property like the one mentioned above would be preferable but does not seem to be available. Since the last reply here is already a few years old there might be better ways to handle this, but I couldn't think of one.
I'm developing a consumer app for a publically avalible rest webservice.
I'm having 2 problems: My proxy and the service authentication.
i cant seem to get past my proxy, actually i do have a valid credential to get by it, but i dont know where or how to provide it!
And second, i also dont know how to responde the basic authentication challenge issued by the web-service...
I do can use it via browser, but i cant get it working on my c# app. Heres the code so far:
HttpClient cli = new HttpClient();
cli.BaseAddress = new Uri("http://myserver.com/");
HttpResponseMessage response = cli.GetAsync("api/service1").Result;
textBox1.Text = response.Content.ReadAsStringAsync().Result;
the result in textBox1 so far is always a 407 error... Can anyone help?
Edit1: Authentication on the webservice is of the type BASIC!
Edit2: clientHandler.Credentials = new NetworkCredential("user", "P#ssw0rd"); does not work... server returns "This request requires HTTP authentication"
Proxy information needs to be configured on the HttpClientHandler object which can be passed into the HttpClient constructor.
var clientHandler = new HttpClientHandler();
clientHandler.Proxy = new WebProxy("http://proxyserver:80/",true);
var httpClient = new HttpClient(clientHandler);
For credentials I do something like this...
var clientHandler = new HttpClientHandler() {PreAuthenticate = true};
var credentialCache = new CredentialCache();
credentialCache.Add(new Uri(Host), "Basic", new NetworkCredential(userName, password));
clientHandler.Credentials = credentialCache;
By setting this up this way, whenever you make a request to any URI that is below the "Host" URI, HttpClientHandler will automatically set the correct authorization header.
Also, be aware there is an alternative handler called WebRequestHandler that can be used instead of HttpClientHandler that add in extra stuff that is only available on the Windows OS like WinINet proxy and Pipelining.