Skip to content

HttpClientGenerator is a tool that uses the Roslyn code generator feature to write boilerplate HttpClient code for you.

License

Notifications You must be signed in to change notification settings

Jalalx/HttpClientCodeGenerator

Repository files navigation

HttpClientGenerator

GitHub Workflow Status (branch) HttpClientGenerator Nuget (with prereleases)

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

  1. Create a console app (net5.0 app)

  2. Install the HttpClientGenerator nuget package

  3. 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 required GetToDoItemByIdAsync 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.

  4. 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.

About

HttpClientGenerator is a tool that uses the Roslyn code generator feature to write boilerplate HttpClient code for you.

Topics

Resources

License

Stars

Watchers

Forks