C# Api Clients¶
C# proxies automatically handle the following stuff for you;
- Maps C# method calls to remote server HTTP calls by considering the HTTP method, route, query string parameters, request payload and other details.
- Authenticates the HTTP Client by adding access token to the HTTP header.
- Serializes to and deserialize from JSON.
- Handles HTTP API versioning.
- Add correlation id, current tenant id and the current culture to the request.
- Properly handles the error messages sent by the server and throws proper exceptions.
This system can be used by any type of .NET client to consume your HTTP APIs.
ABP framework provides two types of client proxy generation system:
-
Dynamic C# Client Proxy
-
Static C# Client Proxy
Static vs Dynamic C# Client Proxies¶
Development-time (static) client proxy generation has a slight performance advantage since it doesn't need to obtain the HTTP API definition on runtime. However, you should re-generate the client proxy code whenever you change your API endpoint definition.
On the other hand, dynamic client proxies are generated on runtime and provides an easier development experience.
Static Client Proxy¶
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project, typically in the Application.Contracts
project.
public interface IIdentityUserAppService : IApplicationService
{
Task<IdentityUserDto> CreateAsync(CreateIdentityUserInput input);
}
Your interface should implement the
IRemoteService
interface to be automatically discovered. Since theIApplicationService
inherits theIRemoteService
interface, theIIdentityUserAppService
above satisfies this condition.
With Contracts or Without Contracts¶
Without Contracts
depending on target service's application.contracts
package, so they can reuse the DTOs and other related classes. However, that can be a problem when we want to create fully independently developed and deployed microservices. We want to use the static proxy generation even without depending target service's application.contracts package.
With Contracts
generate all the classes/enums/other
types in the client side (including application service interfaces) , This is also the default behavior of the generate-proxy
command.
Client Proxy Generation¶
First, add Volo.Abp.Http.Client nuget package to your client project:
Install-Package Volo.Abp.Http.Client
Then add AbpHttpClientModule
dependency to your module:
[DependsOn(
typeof(AbpHttpClientModule) // Add the dependency
)]
public class MyClientAppModule : AbpModule
{
}
Now, it's ready to configure the application for the static client proxy generation.
With Contracts Example (Default behaviour):¶
[DependsOn(
typeof(AbpHttpClientModule), // Used to create client proxies
typeof(AbpVirtualFileSystemModule) // Virtual file system
)]
public class MyClientAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Prepare for static client proxy generation
context.Services.AddStaticHttpClientProxies(
typeof(MyClientAppModule).Assembly
);
// Include the generated app-generate-proxy.json in the virtual file system
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyClientAppModule>();
});
}
}
Without Contracts Example¶
[DependsOn(
typeof(AbpHttpClientModule), // Used to create client proxies
typeof(AbpVirtualFileSystemModule), // Virtual file system
typeof(IdentityApplicationContractsModule) // Contains the application service interfaces
)]
public class MyClientAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Prepare for static client proxy generation
context.Services.AddStaticHttpClientProxies(
typeof(IdentityApplicationContractsModule).Assembly
);
// Include the generated app-generate-proxy.json in the virtual file system
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyClientAppModule>();
});
}
}
AddStaticHttpClientProxies
method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation.
The single application comes pre-configured for the dynamic client proxy generation, in the
HttpApi.Client
project. If you want to switch to the static client proxies, changecontext.Services.AddHttpClientProxies
tocontext.Services.AddStaticHttpClientProxies
in the module class of yourHttpApi.Client
project.
Endpoint Configuration¶
RemoteServices
section in the appsettings.json
file is used to get remote service address by default. The simplest configuration is shown below:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
}
}
}
See the AbpRemoteServiceOptions section below for more detailed configuration.
Code Generation¶
Server side must be up and running while generating the client proxy code. So, run your application that serves the HTTP APIs on the BaseUrl
that is configured like explained in the Endpoint Configuration section.
Open a command-line terminal in the root folder of your client project (.csproj
) and type the following command:
With Contracts¶
abp generate-proxy -t csharp -u https://localhost:44372
If you haven't installed yet, you should install the ABP CLI.
This command should generate the following files under the ClientProxies
folder:
Without Contracts¶
abp generate-proxy -t csharp -u https://localhost:44372 --without-contracts
This command should generate the following files under the ClientProxies
folder:
generate-proxy
command generates proxies for only the APIs you've defined in your application. If you are developing a modular application, you can specify the-m
(or--module
) parameter to specify the module you want to generate proxies.
abp generate-proxy -t csharp -u https://localhost:44372 -m Identity --without-contracts
Dynamic Client Proxy¶
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project, typically in the Application.Contracts
project.
public interface IIdentityUserAppService : IApplicationService
{
Task<IdentityUserDto> CreateAsync(CreateIdentityUserInput input);
}
Your interface should implement the
IRemoteService
interface to be automatically discovered. Since theIApplicationService
inherits theIRemoteService
interface, theIIdentityUserAppService
above satisfies this condition.
Client Proxy Generation¶
The single basic application already comes pre-configured for the client proxy generation, in the
HttpApi.Client
project.
If you're not using a single basic application, then execute the following command in the folder that contains the .csproj
file of your client project:
abp add-package Volo.Abp.Http.Client
If you haven't installed yet, you should install the ABP CLI.
Now, it's ready to create the client proxies. Example:
[DependsOn(
typeof(IdentityApplicationContractsModule), // Contains the application service interfaces
//
typeof(AbpHttpClientModule) // Used to create client proxies
)]
public class IdentityHttpApiClientModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Create dynamic client proxies
context.Services.AddHttpClientProxies(
typeof(IdentityApplicationContractsModule).Assembly
);
}
}
AddHttpClientProxies
method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes.
Endpoint Configuration¶
RemoteServices
section in the appsettings.json
file is used to get remote service address by default. Configuration is shown below:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
}
}
}
Configuration¶
AbpRemoteServiceOptions¶
AbpRemoteServiceOptions
is automatically set from the appsettings.json
by default. Alternatively, you can configure it in the ConfigureServices
method of your module to set or override it. Example:
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
});
//...
}
Multiple Remote Service Endpoints¶
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
},
"Identity": {
"BaseUrl": "http://localhost:48392/"
}
}
}
AddHttpClientProxies
or AddStaticHttpClientProxies
method can get an additional parameter for the remote service name. Example:
You can use const class (e.g. IdentityRemoteServiceConsts.RemoteServiceName
) where you can define service name.
context.Services.AddHttpClientProxies(
typeof(IdentityApplicationContractsModule).Assembly,
IdentityRemoteServiceConsts.RemoteServiceName
);
remoteServiceConfigurationName
parameter matches the service endpoint configured via AbpRemoteServiceOptions
.
Retry/Failure Logic & Polly Integration¶
If you want to add retry logic for the failing remote HTTP calls for the client proxies, you can configure the AbpHttpClientBuilderOptions
in the PreConfigureServices
method of your module class.
Example: Use the Polly library to re-try 3 times on a failure
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
{
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3,
i => TimeSpan.FromSeconds(Math.Pow(2, i))
)
);
});
});
}
This example uses the Microsoft.Extensions.Http.Polly package. You also need to import the Polly
namespace (using Polly;
) to be able to use the WaitAndRetryAsync
method.