Skip to content

Commit 0d3d676

Browse files
committed
Added new document: Static-CSharp-API-Clients
1 parent 613e0ec commit 0d3d676

File tree

3 files changed

+228
-1
lines changed

3 files changed

+228
-1
lines changed

docs/en/API/Dynamic-CSharp-API-Clients.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ Dynamic C# proxies automatically handle the following stuff for you;
1313

1414
This system can be used by any type of .NET client to consume your HTTP APIs.
1515

16+
## Static vs Dynamic Client Proxies
17+
18+
ABP provides **two types** of client proxy generation system. This document explains the **dynamic client proxies**, which generates client-side proxies on runtime. You can also see the [Static C# API Client Proxies](Static-CSharp-API-Clients.md) documentation to learn how to generate proxies on development time.
19+
20+
Development-time (static) client proxy generation has a **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**.
21+
1622
## Service Interface
1723

1824
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 if you've created your solution using the startup templates.
@@ -72,7 +78,7 @@ public class MyClientAppModule : AbpModule
7278
7379
### Endpoint Configuration
7480

75-
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below:
81+
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. The simplest configuration is shown below:
7682

7783
```json
7884
{
@@ -204,3 +210,7 @@ public override void PreConfigureServices(ServiceConfigurationContext context)
204210
````
205211

206212
This example uses the [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly) package. You also need to import the `Polly` namespace (`using Polly;`) to be able to use the `WaitAndRetryAsync` method.
213+
214+
## See Also
215+
216+
* [Static C# Client Proxies](Static-CSharp-API-Clients.md)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Static C# API Client Proxies
2+
3+
ABP can create C# API client proxy code to call your remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level details to call remote services and get results.
4+
5+
Static C# proxies automatically handle the following stuff for you;
6+
7+
* Maps C# **method calls** to remote server **HTTP calls** by considering the HTTP method, route, query string parameters, request payload and other details.
8+
* **Authenticates** the HTTP Client by adding access token to the HTTP header.
9+
* **Serializes** to and deserialize from JSON.
10+
* Handles HTTP API **versioning**.
11+
* Add **correlation id**, current **tenant** id and the current **culture** to the request.
12+
* Properly **handles the error messages** sent by the server and throws proper exceptions.
13+
14+
This system can be used by any type of .NET client to consume your HTTP APIs.
15+
16+
## Static vs Dynamic Client Proxies
17+
18+
ABP provides **two types** of client proxy generation system. This document explains the **static client proxies**, which generates client-side code in your development time. You can also see the [Dynamic C# API Client Proxies](Dynamic-CSharp-API-Clients.md) documentation to learn how to use proxies generated on runtime.
19+
20+
Development-time (static) client proxy generation has a **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**.
21+
22+
## Service Interface
23+
24+
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 if you've created your solution using the startup templates.
25+
26+
Example:
27+
28+
````csharp
29+
public interface IBookAppService : IApplicationService
30+
{
31+
Task<List<BookDto>> GetListAsync();
32+
}
33+
````
34+
35+
> Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition.
36+
37+
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint.
38+
39+
## Client Proxy Generation
40+
41+
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project:
42+
43+
````
44+
Install-Package Volo.Abp.Http.Client
45+
````
46+
47+
Then add `AbpHttpClientModule` dependency to your module:
48+
49+
````csharp
50+
[DependsOn(typeof(AbpHttpClientModule))] //add the dependency
51+
public class MyClientAppModule : AbpModule
52+
{
53+
}
54+
````
55+
56+
Now, it's ready to configure the application for the static client proxy generation. Example:
57+
58+
````csharp
59+
[DependsOn(
60+
typeof(AbpHttpClientModule), //used to create client proxies
61+
typeof(BookStoreApplicationContractsModule) //contains the application service interfaces
62+
)]
63+
public class MyClientAppModule : AbpModule
64+
{
65+
public override void ConfigureServices(ServiceConfigurationContext context)
66+
{
67+
// Prepare for static client proxy generation
68+
context.Services.AddStaticHttpClientProxies(
69+
typeof(BookStoreApplicationContractsModule).Assembly
70+
);
71+
}
72+
}
73+
````
74+
75+
`AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation.
76+
77+
> The [application startup template](../Startup-Templates/Application.md) comes pre-configured for the **dynamic** client proxy generation, in the `HttpApi.Client` project. If you want to switch to the **static** client proxies, change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project.
78+
79+
### Endpoint Configuration
80+
81+
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. The simplest configuration is shown below:
82+
83+
```json
84+
{
85+
"RemoteServices": {
86+
"Default": {
87+
"BaseUrl": "http://localhost:53929/"
88+
}
89+
}
90+
}
91+
```
92+
93+
See the *AbpRemoteServiceOptions* section below for more detailed configuration.
94+
95+
### Code Generation
96+
97+
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.
98+
99+
Open a command-line terminal in the root folder of your client project (`.csproj`) and type the following command:
100+
101+
````bash
102+
abp generate-proxy -t csharp -u http://localhost:53929/
103+
````
104+
105+
> If you haven't installed yet, you should install the [ABP CLI](../CLI.md).
106+
107+
This command should generate the following files under the `ClientProxies` folder:
108+
109+
![generated-static-client-proxies](../images/generated-static-client-proxies.png)
110+
111+
`BookClientProxy.Generated.cs` is the actual generated proxy class in this example. `BookClientProxy` is a `partial` class where you can write your custom code (ABP won't override it). `app-generate-proxy.json` contains information about the remote HTTP endpoint, so ABP can properly perform HTTP requests.
112+
113+
> `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. See the *generate-proxy* section in the [ABP CLI](../CLI.md) documentation for other options.
114+
115+
## Usage
116+
117+
It's straightforward to use the client proxies. Just inject the service interface in the client application code:
118+
119+
````csharp
120+
public class MyService : ITransientDependency
121+
{
122+
private readonly IBookAppService _bookService;
123+
124+
public MyService(IBookAppService bookService)
125+
{
126+
_bookService = bookService;
127+
}
128+
129+
public async Task DoItAsync()
130+
{
131+
var books = await _bookService.GetListAsync();
132+
foreach (var book in books)
133+
{
134+
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
135+
}
136+
}
137+
}
138+
````
139+
140+
This sample injects the `IBookAppService` service interface defined above. The static client proxy implementation makes an HTTP call whenever a service method is called by the client.
141+
142+
## Configuration
143+
144+
### AbpRemoteServiceOptions
145+
146+
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can configure it in the `ConfigureServices` method of your [module](../Module-Development-Basics.md) to set or override it. Example:
147+
148+
````csharp
149+
public override void ConfigureServices(ServiceConfigurationContext context)
150+
{
151+
context.Services.Configure<AbpRemoteServiceOptions>(options =>
152+
{
153+
options.RemoteServices.Default =
154+
new RemoteServiceConfiguration("http://localhost:53929/");
155+
});
156+
157+
//...
158+
}
159+
````
160+
161+
### Multiple Remote Service Endpoints
162+
163+
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:
164+
165+
````json
166+
{
167+
"RemoteServices": {
168+
"Default": {
169+
"BaseUrl": "http://localhost:53929/"
170+
},
171+
"BookStore": {
172+
"BaseUrl": "http://localhost:48392/"
173+
}
174+
}
175+
}
176+
````
177+
178+
`AddStaticHttpClientProxies` method can get an additional parameter for the remote service name. Example:
179+
180+
````csharp
181+
context.Services.AddStaticHttpClientProxies(
182+
typeof(BookStoreApplicationContractsModule).Assembly,
183+
remoteServiceConfigurationName: "BookStore"
184+
);
185+
````
186+
187+
`remoteServiceConfigurationName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
188+
189+
### Retry/Failure Logic & Polly Integration
190+
191+
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.
192+
193+
**Example: Use the [Polly](https://github.com/App-vNext/Polly) library to re-try 3 times on a failure**
194+
195+
````csharp
196+
public override void PreConfigureServices(ServiceConfigurationContext context)
197+
{
198+
PreConfigure<AbpHttpClientBuilderOptions>(options =>
199+
{
200+
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
201+
{
202+
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder =>
203+
policyBuilder.WaitAndRetryAsync(
204+
3,
205+
i => TimeSpan.FromSeconds(Math.Pow(2, i))
206+
)
207+
);
208+
});
209+
});
210+
}
211+
````
212+
213+
This example uses the [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly) package. You also need to import the `Polly` namespace (`using Polly;`) to be able to use the `WaitAndRetryAsync` method.
214+
215+
## See Also
216+
217+
* [Dynamic C# Client Proxies](Dynamic-CSharp-API-Clients.md)
6.95 KB
Loading

0 commit comments

Comments
 (0)