NClient is an automatic, type-safe .NET HTTP client that can call web API methods using annotated interfaces. The main difference between NClient and its analogues is that NClient lets you annotate ASP.NET controllers via interfaces and then use these interfaces to create clients. This allows you get rid of unwanted dependencies on a client side and to reuse an API description in clients without boilerplate code.
// WebService.dll:
public class WeatherController : ControllerBase, IWeatherFacade {
public Task<Weather> GetAsync(string city, DateTime date) => ...;
}
// WebService.Facade.dll:
[HttpFacade, Path("api/[facade]")]
public interface IWeatherFacade {
[GetMethod("{city}")]
Task<Weather> GetAsync([RouteParam] string city, [QueryParam] DateTime date);
}
// Client.dll:
IWeatherFacade weatherFacade = NClientGallery.Clients.GetRest()
.For<IWeatherFacade>(host: "http://localhost:5000")
.WithSafeResilience(maxRetries: 3)
.Build();
Weather todaysWeather = await weatherFacade.GetAsync(city: "Chelyabinsk", date: DateTime.Today);
Do you like it? Support the development of this project and star this repo!
Creating clients for web services can be quite a challenge because, in addition to data transfer, you need to implement query building, serialization, retry policies, mapping, error handling, and logging β not to mention the maintenance that comes with each update of your APIs. What if you could create clients with a fraction of the effort? This is exactly what NClient aims to achieve by allowing you to create clients declaratively.
By the way, you can contribute to the NClient, not just use it :smiley:
Features: Dynamic templated routing; Static routing; Dynamic query parameters; Collections as query parameters; Dynamic headers; Static headers; Dynamic body; Auto serialization and deserialization; HTTP/Transport context; Authentication; Asynchronous requests; Timeouts; Cancellation requests; Resilience policy; Response validation; Response mapping; File upload/download; Generic interfaces; Interface inheritance; Client factory; Versioning; Handling; Structured logging; Dependency injection support.
The easiest way is to install NClient package using Nuget:
dotnet add package NClient
Use of the NClient library requires .NET Standard 2.0 or higher. The NClient controllers can be used with ASP.NET Core and .NET Core 3.1 target or higher.
First, you have to create an interface describing the available endpoints and input/output data of a service via annotations. After that, you can select the required type of client in NClientGallery
and then set additional settings for it if necessary.
If you want to send requests to a third-party service, you should create an interface that describes the service you want to make requests to. Follow the steps below:
NClient
in the client projectdotnet add package NClient
dotnet-nclient
tooldotnet tool install --global dotnet-nclient
dotnet nclient generate facade --api path/to/product-service-swagger.json --output MyProject/Client.cs
This command will generate an interface for the API using the OpenAPI (Swagger) specification:
[Path("api")]
public interface IProductServiceClient
{
[PostMethod("products")]
Task<Product> CreateAsync(Product product);
[GetMethod("products/{id}")]
Task<Product> GetAsync([RouteParam] int id);
}
If necessary, the interface can be changed. It is easy to do this because interface annotation is very similar to the annotation of controllers in ASP.NET. The PathAttribute
defines the base path for all interface methods. The PostMethodAttribute
specifies the type of HTTP method and the path to the endpoint.
Moreover, implicit annotations work as in ASP.NET controllers; for example, the BodyParamAttribute
attribute will be implicitly set to the product parameter in the CreateAsync method. And certainly, route templates are also supported.
Read about all the features in the Annotation and Routing sections.
IProductServiceClient client = NClientGallery.Clients.GetRest()
.For<IProductServiceClient>(host: "http://localhost:8080")
.Build();
The GetRest
method creates a REST client with System.Text.Json
serialization and without a resilience policy.
IProductServiceClient client = NClientGallery.Clients.GetRest()
.For<IProductServiceClient>(host: "http://localhost:8080")
.WithNewtonsoftJsonSerialization()
.WithResilience(x => x
.ForMethod(client => (Func<Product, Task<Product>>) client.CreateAsync)
.Use(maxRetries: 2, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))))
...
.Build();
After calling the For
method, you can configure the client as needed. For example, you can replace the serializer with Newtonsoft.Json
, add a retry policy, and so on. See the full documentation.
// Equivalent to the following request:
// curl -X POST -H "Content-type: application/json" --data "{ id: 1 }" http://localhost:8080/api/products
Product product = await client.CreateAsync(new Product(name: "MyProduct"));
If you want to generate a client for your ASP.NET web service, you need to extract an interface for your controller and annotate it with NClient attributes. These attributes are very similar to those used for ASP.NET controllers. Follow the steps below:
NClient.AspNetCore
package on server-sidedotnet add package NClient.AspNetCore
public class WeatherForecastController : ControllerBase
{
public async Task<WeatherForecast> GetAsync(DateTime date) =>
new WeatherForecast(date: date, temperatureC: -25);
}
Do not annotate your controller with ASP.NET attributes that may cause semantic conflicts with the NClient attributes you intend to use. However, other attributes (including your own) can be used without restrictions.
[HttpFacade, Path("[controller]")] // equivalent to [ApiController, Route("[controller]")]
public interface IWeatherForecastController
{
[GetMethod] // equivalent to [HttpGet]
Task<WeatherForecast> GetAsync([QueryParam] DateTime date); // equivalent to [FromQuery]
}
public class WeatherForecastController : ControllerBase, IWeatherForecastController { ... }
The annotation in the interface instead of the controller allows you to place the interface in a separate assembly. Consequently, the client using this interface doesn't depend on the ASP.NET application.
public interface IWeatherForecastClient : IWeatherForecastController { }
You should do it if you want your client type not to contain "Controller" in the name or if you want to override some methods for the client (see OverrideAttribute
in Annotation section).
There is no need to duplicate interface attributes, as they are inherited.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddNClientControllers();
}
The AddNClientControllers
method can be used in combination with the AddControllers
method. This allows you to use standard ASP.NET controllers together with NClient controllers in the same application.
NClient
package on client-sidedotnet add package NClient
IWeatherForecastController client = NClientGallery.Clients.GetRest()
.For<IWeatherForecastController>(host: "http://localhost:8080")
.Build();
If you decide to follow the 4th step, use the IWeatherForecastClient
interface instead of IWeatherForecastController
.
// Equivalent to the following request:
// curl -X GET -H "Content-type: application/json" http://localhost:8080/WeatherForecast?date=2021-03-13T00:15Z
WeatherForecast forecast = await client.GetAsync(DateTime.Now);
You can find NClient documentation on the Wiki.
See samples of applications in the NClient.Samples project.
Youβre thinking about contributing to NClient? Great! We love to receive contributions from the community! The simplest contribution is to give this project a star β.
Helping with documentation, pull requests, issues, commentary or anything else is also very welcome. Please review our contribution guide.
It's worth getting in touch with us to discuss changes in case of any questions. We can also give advice on the easiest way to do things.
The NClient wishes to thank JetBrains for supporting the project with free open source Rider licenses.