Skip to content

Common HTTP interfaces for API "client side" support, in .NET Core 3.0

License

Notifications You must be signed in to change notification settings

netsoft-ruidias/api-client-3.0

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub .Net Core Version GitHub Workflow Status GitHub last commit

Netsoft Infrastructure API Client 3.0

Common HTTP interfaces for API "client side" support
This is a new implementation proposal based in some pre established requisites

What is done

Prerequisites

  • Build with the new .NETCore 3.0
  • Has Resilience with Polly
  • Has Stream serialization / deserialization for performance
  • Support fluent for client configuration
  • Minimum dependencies (only one package)

And also:

  • HttpClient life cycle based on HttpClientFactory (as best practice recommended by Microsoft)
  • No dependency on "Newtonsoft", instead rely on "System.Text.Json.JsonSerializer" (netcore3 core) which has better performance
  • Minimum effort to setup and run
  • Implemented strategy for dependency injection configuration
  • Implemented strategy (cleaner and transparent) for identity token injection
  • Every operations are async (including serialization/deserialization) to avoid thread locks
  • Using "ResponseHeadersRead" (and stream deserialization) for improved performance

What still needs to be done

  • Some code needs refactoring (especially "OAuthTokenProvider" that has some harcoded values)
  • Only "GET" and "POST" were implemented, need to do the same for "PUT", "DELETE", "PATCH" and "HEAD" (almost just copy-paste code)
  • Identity build on IdentityServer4, needs other strategies
  • Identity only supports "client_credentials", need to implement other strategies
  • Unit Tests

What is not done

  • Handle exceptions (Coming soon or read my suggestion below)

Getting started

For microservices developers

In your microservice solution, create an ClientSDK project and then follow this simple steps

Step 1

Install the new package in your "Client.SDK" project
(this is the only dependency you will need)

dotnet add package Netsoft.Core.ApiClient.3.0.0

Step 2

Create a new class for your client

public class DemoClientSDK
{ }

Inherit from "HttpClientBase" and create your own interface for DI
The constructor must receive and "HttpClient" which will be injected by DI

public class DemoClientSDK : HttpClientBase, IDemoClientSDK
{ 
    public DemoClientSDK(HttpClient httpClient)
        : base(httpClient)
    { }
}

Implement your own methods:

public class DemoClientSDK : HttpClientBase, IDemoClientSDK
{ 
    public DemoClientSDK(HttpClient httpClient)
        : base(httpClient)
    { }

    public async Task<FooResponse> GetFooAsync(int id)
    {
        var result = await this.GetAsync<FooResponse>($"api/Values/{id}");
        return result.Body;
    }
}

Step 3

Create a new static class for your DI configuration

public static class ServiceConfigExtensions
{ }

Create an extension for "IServiceCollection"

public static class ServiceConfigExtensions
{ 
    public static IServiceCollection AddDemoClient(
        this IServiceCollection services, 
        Uri serviceBaseAddress, 
        IPolicyConfig policyConfig, 
        IIdentityConfig identityConfig)
    {
        return services;
    }
}
  • IServiceCollection is the unity DI services configuration collection
  • Uri should be your service url
  • IPolicyConfig is supplied by the netsoft package Netsoft.Core.ApiClient.3.0 and it is intended to inject resilience configurations
  • IIdentityConfig is supplied by netsoft package Netsoft.Core.ApiClient.3.0 and it is intended to inject indentity configurations

Be aware that all configurations must be supplied by the consumer

Configure your service client
(this configurations may vary according to your own needs)

public static class ServiceConfigExtensions
{ 
    public static IServiceCollection AddDemoClient(
        this IServiceCollection services, 
        Uri serviceBaseAddress, 
        IPolicyConfig policyConfig, 
        IIdentityConfig identityConfig)
    {
        services.AddHttpClient<IDemoClientSDK, DemoClientSDK>(x =>
        {
            x.BaseAddress = serviceBaseAddress;
            x.DefaultRequestHeaders.Add("Accept", "application/json");
            x.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
            x.DefaultRequestVersion = new Version(2, 0);
        })
        .AddResilience(policyConfig)
        .AddIdentity(identityConfig, new TokenStrategy());
        
        return services;
    }
}

For microservices consumers

Microservices consumers have their life easier

In your Gateway project, create a new static class for your DI configuration

public static class DataGatewayExtensions
{ }

Create an extension for "IServiceCollection"

public static class ServiceConfigExtensions
{ 
    public static IServiceCollection AddDataGateways(this IServiceCollection services)
    {
        return services;
    }
}

Reference or install the ClientDSK package and use their own extension to configure the client, like this:

public static class ServiceConfigExtensions
{ 
    public static IServiceCollection AddDataGateways(this IServiceCollection services)
    {
        var serviceProvider = services.BuildServiceProvider();

        var policyConfig = serviceProvider.GetRequiredService<IPolicyConfig>();
        var identityConfig = serviceProvider.GetRequiredService<IIdentityConfig>();

        services.AddDemoClient(
            new Uri("https://localhost:44348"),
            policyConfig,
            identityConfig);

        return services;
    }
}

Then, in your "Presentation" project simply bind your IPolicyConfig and IIdentityConfig with the configuration file and call then ".AddDataGateways" extension

public static class ApiExtensions
{ 
    public static IServiceCollection AddSettings(this IServiceCollection services)
    {
        services.AddSingleton<IPolicyConfig>(serviceProvider =>
        {
            var config = serviceProvider.GetRequiredService<IConfiguration>();
            var policyConfig = new PolicyConfig();

            config.Bind("PolicyConfig", policyConfig);

            return policyConfig;
        });

        services.AddSingleton<IIdentityConfig>(serviceProvider =>
        {
            var config = serviceProvider.GetRequiredService<IConfiguration>();
            var identityConfig = new IdentityConfig();

            config.Bind("IdentityConfig", identityConfig);

            return identityConfig;
        });

        return services;
    }
}

use it

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions();

        services.AddMvc()

        //(...Swagger...etc...)

        services
            .AddSettings()          // from Presentation project
            .AddDataGateways();     // from Gateway project
    }
}

Handling Exceptions

As a best practice, Microsoft recommends the use of a middleware in replacement of the old handlers

Add the ExceptionHandler middleware to the pipeline using the Configure method of the startup class

public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UsePingPong()
           .StartSwagger();

        app.UseMiddleware<ExceptionsMiddleware>();

        app.UseMvc();

        return app;
    }
}

Contributing

Contributors welcome!

Thanks for taking an interest in the library and the github community!

  1. Clone this repo
  2. Make some changes
  3. Make a pull request

Licence

MIT

About

Common HTTP interfaces for API "client side" support, in .NET Core 3.0

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages