Awesome
HttpClientGenerator
HttpClientGenerator is a tool that uses Roslyn source generator feature to write boilerplate HttpClient code for you.
The Philosophy
You should not write or generate boilerplate code. Your repository should not host or track auto-generated code.
This leaves you with:
- A much cleaner codebase
- No need for calling generator tool everytime HTTP contracts change
- You do not need to maintain or have a separate tool around out of your repo
- And no dependency on any 3rd-party code at runtime!
Installing
dotnet add package HttpClientGenerator
Usage
-
Create a console app (net5.0 app)
-
Install the
HttpClientGenerator
nuget package -
Add following code to your project:
//using area using HttpClientGenerator.Shared; using System.Text.Json.Serialization; // ... namespace ConsoleClientApp { // Make sure to mark the class and method as partial! public partial class ToDoHttpService { [HttpGet("todos/{id}")] public partial Task<ToDoItem> GetToDoItemByIdAsync(int id); } public class ToDoItem { [JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("firstName")] public int? UserId { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("completed")] public bool IsCompleted { get; set; } } }
Notice to the
partial
keyword on class and method definition. The library generates the requiredGetToDoItemByIdAsync
method for you. All you need to do is to call the method like this:static class Program { static async Task Main(string[] args) { // Todo: Use HttpClientFactory to create HttpClient instance. Read more at: // https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests using (var client = new HttpClient()) { client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com"); // The tool will generate a constructor with HttpClient argument for you var todoService = new ToDoHttpService(client); // Simply call the partial method, the tool has generated the required code for you var item = await todoService.GetToDoItemByIdAsync(1); Console.WriteLine($"Task {item.Title}: completed: {item.IsCompleted}"); } Console.Read(); } }
Behind the scene, the tool generates following code for you:
// <auto-generated> // This code was generated by HttpClientCodeGenerator. // </auto-generated> using System; using System.Net.Http; using System.Threading.Tasks; using System.Collections.Generic; using ConsoleClientApp; using HttpClientGenerator.Shared; namespace ConsoleClientApp { public partial class ToDoHttpService { protected readonly HttpClient _httpClient; public ToDoHttpService(HttpClient httpClient) { _httpClient = httpClient; } public partial async Task<ToDoItem> GetToDoItemByIdAsync(int id) { const string @___httpMethod = "GET"; var @___path = "todos/{id}"; var @___routes = new Dictionary<string, object>(); @___routes["id"] = id; var @___queryParams = new Dictionary<string, object>(); // Query String dictionary goes here... var @___headers = new Dictionary<string, string>(); // Header dictionary goes here... return await HttpClientGenerator.Shared.HttpClientHelper.SendAsync<ToDoItem>(_httpClient, @___httpMethod, @___path, @___headers, @___routes, @___queryParams); } } }
NOTE: This code is generated on the fly and is not written on disc so you don't have to worry about adding it to source control or debugging it.
-
Now you can build and run!
// output
Task delectus aut autem: completed: False
It's cool, isn't it?
Default JSON serializer
You can change the default JsonSerializerOptions
by modifying the singleton reference at HttpClientHelper.DefaultJsonSerializerOptions
. In the default implementation JsonSerializerOptions.PropertyNameCaseInsensitive
is set to true
to cover most use cases.
About HttpClient
injection
The tool automatically adds a constructor with a HttpClient
parameter in the generated code but you can provide
a HttpClient
instance manually by using one of the following methods:
Introducing a field or property
If you add a constructor and provide a HttpClient
field or property, the generator will use that field:
public partial class ToDoHttpService
{
// The generator will pick up this field (or Property)
protected readonly HttpClient _httpClient;
// protected HttpClient HttpClient { get; } = new HttpClient();
public ToDoHttpService(HttpClient httpClient)
{
_httpClient = httpClient;
}
// ... rest of code
}
Introducing a method
If you have a parameterless method that returns HttpClient
, the generator will pick it up:
public partial class ToDoHttpService
{
// The generator will call this method when needs a HttpClient instance
private HttpClient MyCustomHttpClientResolver()
{
var client = new HttpClient();
// initialize the client here...
return client;
}
// ... rest of code
}
Recommended way:
We strongly suggest to use IHttpClientFactory
to resolve HttpClient
instances. You can read more about it here: Use IHttpClientFactory to implement resilient HTTP requests
Known Issues
- You will currently need to restart Visual Studio 2019 to see IntelliSense and get rid of errors with the early tooling experience.
- During development of this library using Visual Studio, make sure you have read this issue.
- If you are using Visual Studio Code (Omnisharp C# extension) you might see some red lines under the partial method. Until now, there is no full support for this feature on Omnisharp, but dotnet SDK will work without problem.
Please feel free to open issue for reporting a bug or missing feature.