Problem
How to consume ASP.NET Core Web API using HttpClient
.
Solution
We’ll create a library to wrap the functionality of HttpClient
. I’ll use builder pattern for this purpose. Add a class with methods for storing parts of HttpClient
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public class HttpRequestBuilder { private HttpMethod method = null; private string requestUri = ""; private HttpContent content = null; private string bearerToken = ""; private string acceptHeader = "application/json"; private TimeSpan timeout = new TimeSpan(0, 0, 15); public HttpRequestBuilder() { } public HttpRequestBuilder AddMethod(HttpMethod method) { this.method = method; return this; } public HttpRequestBuilder AddRequestUri(string requestUri) { this.requestUri = requestUri; return this; } public HttpRequestBuilder AddContent(HttpContent content) { this.content = content; return this; } public HttpRequestBuilder AddBearerToken(string bearerToken) { this.bearerToken = bearerToken; return this; } public HttpRequestBuilder AddAcceptHeader(string acceptHeader) { this.acceptHeader = acceptHeader; return this; } public HttpRequestBuilder AddTimeout(TimeSpan timeout) { this.timeout = timeout; return this; } // rest of code |
Add a method to send request using HttpClient
and return the response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public async Task<HttpResponseMessage> SendAsync() { // Check required arguments EnsureArguments(); // Setup request var request = new HttpRequestMessage { Method = this.method, RequestUri = new Uri(this.requestUri) }; if (this.content != null) request.Content = this.content; if (!string.IsNullOrEmpty(this.bearerToken)) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.bearerToken); request.Headers.Accept.Clear(); if (!string.IsNullOrEmpty(this.acceptHeader)) request.Headers.Accept.Add( new MediaTypeWithQualityHeaderValue(this.acceptHeader)); // Setup client var client = new System.Net.Http.HttpClient(); client.Timeout = this.timeout; return await client.SendAsync(request); } |
We’ll also add a factory class to build requests for GET, POST, PUT, PATCH
and DELETE
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public static class HttpRequestFactory { public static async Task<HttpResponseMessage> Get(string requestUri) { var builder = new HttpRequestBuilder() .AddMethod(HttpMethod.Get) .AddRequestUri(requestUri); return await builder.SendAsync(); } public static async Task<HttpResponseMessage> Post( string requestUri, object value) { var builder = new HttpRequestBuilder() .AddMethod(HttpMethod.Post) .AddRequestUri(requestUri) .AddContent(new JsonContent(value)) ; return await builder.SendAsync(); } public static async Task<HttpResponseMessage> Put( string requestUri, object value) { var builder = new HttpRequestBuilder() .AddMethod(HttpMethod.Put) .AddRequestUri(requestUri) .AddContent(new JsonContent(value)); return await builder.SendAsync(); } public static async Task<HttpResponseMessage> Patch( string requestUri, object value) { var builder = new HttpRequestBuilder() .AddMethod(new HttpMethod("PATCH")) .AddRequestUri(requestUri) .AddContent(new PatchContent(value)); return await builder.SendAsync(); } public static async Task<HttpResponseMessage> Delete(string requestUri) { var builder = new HttpRequestBuilder() .AddMethod(HttpMethod.Delete) .AddRequestUri(requestUri); return await builder.SendAsync(); } } |
JsonContent
, PatchContent
and FileContent
are custom class to simplify sending of data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class JsonContent : StringContent { public JsonContent(object value) : base (JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json") { } public JsonContent(object value, string mediaType) : base(JsonConvert.SerializeObject(value), Encoding.UTF8, mediaType) { } } public class PatchContent : StringContent { public PatchContent(object value) : base (JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json-patch+json") { } } public class FileContent : MultipartFormDataContent { public FileContent(string filePath, string apiParamName) { var filestream = File.Open(filePath, FileMode.Open); var filename = Path.GetFileName(filePath); Add(new StreamContent(filestream), apiParamName, filename); } } |
Finally few extension methods to help working with HttpResponseMessage
class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static class HttpResponseExtensions { public static T ContentAsType<T>(this HttpResponseMessage response) { var data = response.Content.ReadAsStringAsync().Result; return string.IsNullOrEmpty(data) ? default(T) : JsonConvert.DeserializeObject<T>(data); } public static string ContentAsJson(this HttpResponseMessage response) { var data = response.Content.ReadAsStringAsync().Result; return JsonConvert.SerializeObject(data); } public static string ContentAsString(this HttpResponseMessage response) { return response.Content.ReadAsStringAsync().Result; } } |
We can use the code above like:
1 2 3 4 5 6 7 8 9 10 |
private static async Task GetList() { var requestUri = $"{baseUri}"; var response = await HttpRequestFactory.Get(requestUri); Console.WriteLine($"Status: {response.StatusCode}"); var outputModel = response.ContentAsType<List<MovieOutputModel>>(); outputModel.ForEach(item => Console.WriteLine("{0} - {1}", item.Id, item.Title)); } |
Here is how the sample client looks like:
Note: sample code has examples of other type of requests too.
Note: sample code uses CRUD API created in a previous post. Download it’s project and run the API before running this console sample.
Source Code
GitHub: https://github.com/TahirNaushad/Fiver.Api.HttpClient
Good stuff! Helped me a lot when it came down to integration testing. Thanks!
Helped me a lot , thanks
Thanks for the post, it’s been very useful. I can’t seem to find the implementation of EnsureArguments(). Could you add it to your post?
private void EnsureArguments()
{
if (this.method == null)
throw new ArgumentNullException(“Method”);
if (string.IsNullOrEmpty(this.requestUri))
throw new ArgumentNullException(“Request Uri”);
}