Set HTTP protocol version in HttpClient - c#

I need to make a request to a webservice that uses HTTP version 1.0. Im using HttpClient , But I cant see any option to set HTTP version.
Where can i set the request version?

In order to set the version you'll have to create an instance of HttpRequestMessage and set its Version property which you pass to HttpClient.SendAsync. You can use the helper HttpVersion utility class:
var requestMessage = new HttpRequestMessage
{
Version = HttpVersion.Version10
};
var client = new HttpClient();
var response = await client.SendAsync(requestMessage);

HTTP version is sent as a header in every request, so it is set in the message sent by System.Net.Http.HttpClient: see the ProtocolVersion property of the HttpWebRequest class.

Related

System.FormatException when adding User-Agent with colon

I am trying to make a http request like this one https://tools.keycdn.com/geo.json?host=google.com in C#. For this, according to the https://tools.keycdn.com/geo?host=google.com, I have to add user agent like this
User-Agent: keycdn-tools:https://myownsite.com
I am doing the request with this code:
HttpClient? client = new HttpClient();
// Add correct user agent.
var request = new HttpRequestMessage(HttpMethod.Get, "https://tools.keycdn.com/geo.json?host=" + textBoxDomain.Text);
var useragentValue = new ProductInfoHeaderValue("keycdn-tools:https://myownsite.com");
request.Headers.UserAgent.Add(useragentValue);
var result = await client.SendAsync(request);
But I get the System.FormatException when adding the user agent.
How can I resolve this problem?
Edit
I tried to solve the problem by adding DefaultRequestHeaders directly to client object, but I got the same error message
Then I tried to add the header to the request, but there I got also the same error message
.NET's ProductInfoHeaderValue helper class is enforcing syntax restrictions that the HTTP specification doesn't require for User-Agent because the purpose of this class is to create a value that follows the RFC's specification for product tokens.
You can instead add User-Agent directly to the Headers collection:
request.Headers.Add("User-Agent", "keycdn-tools:https://myownsite.com");
If you wish to set the same user agent for every request made by that request instance:
client.DefaultRequestHeaders.Add("User-Agent", "my-user-agent-name");

HttpClient.GetStreamAsync() with custom request?

My goal is to use the HttpClient class to make a web-request so that I can write the response to a file (after parsing). Therefore I need the result as a Stream.
HttpClient.GetStreamAsync() only takes the string requestUri as parameter. So there is no possibility to create a request with custom HttpRequestHeader, custom HttpMethod, custom ContentType, custom content and so on?
I saw that HttpWebRequest is sometimes used instead, but in my PCL (Profile111) there is no Add method for the Headers. So can I use HttpClient, should I use HttpWebRequest instead or should I use another class/library at all?
GetStreamAsync is just a shortcut for building and sending a content-less GET request. Doing it the "long way" is fairly straightforward:
var request = new HttpRequestMessage(HttpMethod.???, uri);
// add Content, Headers, etc to request
request.Content = new StringContent(yourJsonString, System.Text.Encoding.UTF8, "application/json");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
Since you mentioned being open to using a different library, here's an alternative that uses Flurl (disclaimer: I'm the author). Say you want to POST some JSON data with a couple custom headers and receive a stream:
var stream = await "https://api.com"
.WithHeaders(new { header1 = "value1", header2 = "value2" })
.PostJsonAsync(data)
.ReceiveStream();
Here data is just a POCO. Don't worry about serializing it to a JSON string or setting Content-Type to application/json; Flurl will do both for you.
Flurl uses HttpClient under the hood and targets .NET Standard 1.1, which is fully compatible with PCL Profile111.

C# How to pass on a cookie using a shared HttpClient

I have the following set up:
JS client -> Web Api -> Web Api
I need to send the auth cookie all the way down. My problem is sending it from one web api to another. Because of integration with an older system, that uses FormsAuthentication, I have to pass on the auth cookie.
For performance reasons I share a list of HttpClients (one for each web api) in the following dictionary:
private static ConcurrentDictionary<ApiIdentifier, HttpClient> _clients = new ConcurrentDictionary<ApiIdentifier, HttpClient>();
So given an identifier I can grab the corresponding HttpClient.
The following works, but I'm pretty sure this is bad code:
HttpClient client = _clients[identifier];
var callerRequest = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
string authCookieValue = GetAuthCookieValue(callerRequest);
if (authCookieValue != null)
{
client.DefaultRequestHeaders.Remove("Cookie");
client.DefaultRequestHeaders.Add("Cookie", ".ASPXAUTH=" + authCookieValue);
}
HttpResponseMessage response = await client.PutAsJsonAsync(methodName, dataToSend);
// Handle response...
Whats wrong about this is that 1) it seems wrong to manipulate DefaultRequestHeaders in a request and 2) potentially two simultanious requests may mess up the cookies, as the HttpClient is shared.
I've been searching for a while without finding a solution, as most having a matching problem instantiates the HttpClient for every request, hence being able to set the required headers, which I'm trying to avoid.
At one point I had get requests working using a HttpResponseMessage. Perhaps that can be of inspiration to a solution.
So my question is: is there a way to set cookies for a single request using a HttpClient, that will be safe from other clients using the same instance?
Instead of calling PutAsJsonAsync() you can use HttpRequestMessage and SendAsync():
Uri requestUri = ...;
HttpMethod method = HttpMethod.Get /*Put, Post, Delete, etc.*/;
var request = new HttpRequestMessage(method, requestUri);
request.Headers.TryAddWithoutValidation("Cookie", ".ASPXAUTH=" + authCookieValue);
request.Content = new StringContent(jsonDataToSend, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
UPDATE:
To make sure that your HTTP client does not store any cookies from a response you need to do this:
var httpClient = new HttpClient(new HttpClientHandler() { UseCookies = false; });
Otherwise you might get unexpected behavior by using one client and sharing other cookies.

HttpClient request header customisation

Is it possible to set the request ACCEPT header of the HttpClient in .Net/Web Api to include "application/json;odata=verbose"?
I know how to set the request media type
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
But how do I set the odata=verbose part? I cannot seem to find any solutions online to do that.
Do I have to use HttpWebRequest instead? Basically I need to call sharepoint 2013 rest api, and that odata=verbose part is required.
MediaTypeWithQualityHeaderValue has a property called Parameters to which you can add 'odata=verbose' parameter.
Other easy way is to call MediaTypeWithQualityHeaderValue's Parse/TryParse methods to which you can supply the whole "application/json;odata=verbose" media type string.
Here is an example using Parse
using (HttpClient httpClient = new HttpClient())
{
//Setup Accept Header
MediaTypeWithQualityHeaderValue acceptHeader = MediaTypeWithQualityHeaderValue.Parse("application/json;odata=verbose");
httpClient.DefaultRequestHeaders.Accept.Add(acceptHeader);
//... do other stuff
}

HttpClient.GetAsync with network credentials

I'm currently using HttpWebRequest to get a website. I'd like to use the await pattern, which is not given for HttpWebRequests. I found the class HttpClient, which seems to be the new Http worker class. I'm using HttpClient.GetAsync(...) to query my webpage. But I'm missing the option to add ClientCredentials like HttpWebRequest.Credentials. Is there any way to give the HttpClient authentication information?
You can pass an instance of the HttpClientHandler Class with the credentials to the HttpClient Constructor:
using (var handler = new HttpClientHandler { Credentials = ... })
using (var client = new HttpClient(handler))
{
var result = await client.GetAsync(...);
}
You shouldn't dispose of the HttpClient every time, but use it (or a small pool of clients) for a longer period (lifetime of application. You also don't need the handler for it, but instead you can change the default headers.
After creating the client, you can set its Default Request Headers for Authentication. Here is an example for Basic authentication:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "username:password".ToBase64());
ToBase64() represents a helper function that transforms the string to a base64 encoding.

Categories

Resources