HTTP means Hyper Text Transfer Protocol. It’s the underlying protocol used by the World Wide Web and it defines the formatting of transmitted messages between client and server. This protocol was designed for distributed, collaborative and hypermedia information systems.
In 2015 the Internet Engineering Task Force (IETF) released HTTP/2, the second major version of this protocol with several important goals in mind:
- Request multiplexing – sending multiple requests for data in parallel over a single TCP connection
- Protocol negotiation (HTTP/1.1 or other)
- Compatibility with HTTP/1.1
- Compression of request headers
- Binary protocol
- HTTP/2 Server Push
- Request pipelining
- HOL blocking
Note that HTTP/2 is supported in .NET Core however several configurations must be met. For more information you can check out the following links
The httpclient class contain all the functionality of delivering messages using the http protocol. You can pass in an absolute url and let the subsequent calls have relative paths or you can create an new instance with no path and pass on each call the absolute url.
var client = new HttpClient() { BaseAddress = new Uri("https://developer.api.autodesk.com/") };
Contents
The flow of sending a request and handling a response is easy. The request is defined using the HttpRequestMessage and the response in the HttpResponseMessage class.
A. FormUrlEncodedContent:
Sending form data is made using the FormUrlEncodedContent class, which wraps the keys and values as a list of KeyValuePairs.
string relativeUri = "authentication/v1/authenticate"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUri); request.Headers.Clear(); request.Content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("client_id",FORGE_CLIENT_ID), new KeyValuePair<string, string>("client_secret",FORGE_CLIENT_SECRET), new KeyValuePair<string, string>("grant_type","client_credentials"), new KeyValuePair<string, string>("scope",scopesStr) }); HttpResponseMessage response = await _client.SendAsync(request).ConfigureAwait(false);
B. StringContent:
The string content allows you to send a string in your response. This string in most use cases is a serialized json object that you want to send to your server and along with it you will send the content type. It’s recommended to explicitly set the content type your self and this can be a predefined one or you can have a custom type defining the name and the version of the content you are sending.
string serialized = JsonConvert.SerializeObject(person); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUri); request.Headers.Clear(); request.Headers.Add("Authorization", "Bearer " + bearer.AccessToken); request.Content = new StringContent(serialized); request.Content.Headers.Clear(); request.Content.Headers.Add("Content-Type", "application/json");
C. StreamContent:
The stream content allows you to send binary data to the server. You can achieve a similar action by using ByteArrayContent, the difference is that a stream content sends data in smaller chunk sizes which makes them available as soon as they are send.
using (FileStream stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { string relativeUri = $"buckets/myimage.png"; int.TryParse(stream.Length.ToString(), out int fileLength); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, relativeUri); request.Headers.Clear(); request.Headers.Add("Authorization", "Bearer " + bearer.AccessToken); request.Content = new StreamContent(stream); request.Content.Headers.Clear(); request.Content.Headers.Add("Content-Length", $"{fileLength}"); request.Content.Headers.Add("Content-Type", "image/png"); HttpResponseMessage response = await _client.SendAsync(request).ConfigureAwait(false); }
Mocking
Unit testing is absolutely important in any application development, and mocking a response is not far from it. First of all, let’s take a look at the source code.
The HttpClient class has 3 constructors, one is passing the HttpClientHandler instance and the other two allow you to pass an HttpMessageHandler.
The HttpMessageHandler is an abstract base class and the HttpClientHandler is the default implementation. This construct allows use to have 2 scenarions:
- In testing we can use a FakeHandler that implements the HttpMessageHandler
- In production we can use the HttpClientHandler
public class WorkHttpClient : HttpClient { public WorkHttpClient(HttpMessageHandler httpMessageHandler) : base(httpMessageHandler, true) { } } public class FakeMessageHandler : HttpMessageHandler { protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent("Hello") }; } } class Program { static async Task Main(string[] args) { var serviceCollection = new ServiceCollection(); serviceCollection.AddScoped<HttpClient, WorkHttpClient>(); serviceCollection.AddScoped<HttpMessageHandler, FakeMessageHandler>(); ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); HttpClient client = serviceProvider.GetRequiredService(); HttpResponseMessage response =await client.GetAsync("http://localhost/testfakeclient"); var respContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"{respContent} World!"); } }