# Consuming REST APIs from a .NET Client Using ABP's Client Proxy System
In this article, we will show how to consume Rest API by creating a new project and converting that from the dynamic client proxy to the static client proxy. Also, I will glance at the differences and similarities between static and dynamic generic proxies.
Article flow
* Create a new ABP application with ABP CLI
* Create application service interface
* Implement the application service
* Consume the app service from the console application
* Convert application to use static client proxies
* Add authorization to the application service endpoint
* Grant the permission
* Further reading
### Create a new ABP application with ABP CLI
Firstly create a new template via ABP CLI.
# Consuming HTTP APIs from a .NET Client Using ABP's Client Proxy System
````shell
abp new Acme.BookStore -t app
````
> If you haven't installed it yet, you should install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI).
In this article, I will show how to consume your HTTP APIs from a .NET application using ABP's [dynamic](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients) and [static](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients) client-side proxy systems. I will start by creating a new project and consume the HTTP APIs from a .NET console application using dynamic client proxies. Then I will switch to static client proxies. Finally, I will glance at the differences and similarities between static and dynamic generic proxies.
After restoring the project will download the NuGet packages.
## Create a new ABP application with the ABP CLI
Firstly create a new solution via [ABP CLI](https://docs.abp.io/en/abp/latest/CLI):
Now you should run the DbMigrator project to up your database and your project is ready for running.
````shell
abp new Acme.BookStore
````
From now on, we will add some files to show the case to you.
> See ABP's [Getting Started document](https://docs.abp.io/en/abp/latest/Getting-Started-Setup-Environment?UI=MVC&DB=EF&Tiered=No) to learn how to create and run your application, if you haven't done it before.
### Create application service interface
Define an interface for the application service. Create an `IBookAppService` interface in the `Books` folder (namespace) of the `Acme.BookStore.Application.Contracts` project:
## Create application service interface
I will start by creating an application service and expose it as HTTP API to be consumed by remote clients. First, define an interface for the application service; Create an `IBookAppService` interface in the `Books` folder (namespace) of the `Acme.BookStore.Application.Contracts` project:
````csharp
using System.Threading.Tasks;
@ -38,12 +23,12 @@ namespace Acme.BookStore.Books
{
public interface IBookAppService : IApplicationService
Create a `Books` folder (namespace) in the `Acme.BookStore.Application.Contracts` project and add a `BookDto` class inside it:
Also add a `BookDto` class inside the same `Books` folder:
```csharp
using System;
@ -51,20 +36,16 @@ using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Books
{
public class BookDto : AuditedEntityDto<Guid>
public class BookDto
{
public string AuthorName { get; set; }
public string Name { get; set; }
public DateTime PublishDate { get; set; }
public string AuthorName { get; set; }
public float Price { get; set; }
}
}
```
# Implement the application service
## Implement the application service
It is time to implement the `IBookAppService` interface. Create a new class, named `BookAppService` in the `Books` namespace (folder) of the `Acme.BookStore.Application` project:
```csharp
@ -79,7 +60,7 @@ namespace Acme.BookStore.Books
{
public class BookAppService : ApplicationService, IBookAppService
{
public Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
public Task<PagedResultDto<BookDto>> GetListAsync()
{
var bookDtos = new List<BookDto>()
{
@ -95,10 +76,10 @@ namespace Acme.BookStore.Books
}
}
```
It simply returns a list of books. You probably want to get the books from a database, but it doesn't matter for this article. To do it you can visit [here](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)
It simply returns a list of books. You probably want to get the books from a database, but it doesn't matter for this article. If you want it, you can fully implement [this tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF).
### Consume the app service from the console application
Change `ClientDemoService` as shown the following in the `Acme.BookStore.HttpApi.Client.ConsoleTestApp` project under the test folder.
## Consume the app service from the console application
The startup solution comes with an example .NET console application (`Acme.BookStore.HttpApi.Client.ConsoleTestApp`) that is fully configured to consume your HTTP APIs remotely. Change `ClientDemoService` as shown the following in the `Acme.BookStore.HttpApi.Client.ConsoleTestApp` project (it is under the `test` folder).
```csharp
using Acme.BookStore.Books;
@ -127,13 +108,19 @@ public class ClientDemoService : ITransientDependency
}
```
The output should be
> Books: Anna Karenina, Crime and Punishment, Mother
We are basically injecting the `IBookAppService` interface to consume the remote service. ABP handles all the details (performing HTTP request, deserializing the resulting JSON object, etc) for us.
You can run the application to see the output:
````
Books: Anna Karenina, Crime and Punishment, Mother
````
### Convert application to use static client proxies
## Convert application to use static client proxies
Before showing you how to use static client proxies instead of dynamic client proxies, I ask you to talk differences between both approaches. Their similarities, advantages and disadvantages to each other.
##### Benefits
**Benefits (both valid for dynamic and static proxies):**
* 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.
@ -141,16 +128,11 @@ Before showing you how to use static client proxies instead of dynamic client pr
* 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.
##### Differences
Static generic proxies provide better performance because it doesn't need to run on runtime, but you should **re-generate** once changing API endpoint definition. Dynamic generic proxies don't need **re-generate** again because it works on the runtime but it happens more a bit time.
**Differences of dynamic and static proxies:**
Now focus on how to do it,
Firstly add Volo.Abp.Http.Client NuGet package to your client project:
````shell
Install-Package Volo.Abp.Http.Client
````
Static generic proxies provide **better performance** because it doesn't need to run on runtime, but you should **re-generate** once changing the API endpoint definition. Dynamic generic proxies don't need re-generate again because it works on the runtime but it has a slight performance penalty.
> The [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/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, change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project.
The [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/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, you should change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project:
```csharp
public class BookStoreHttpApiClientModule : AbpModule
@ -159,7 +141,7 @@ public class BookStoreHttpApiClientModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
@ -172,24 +154,28 @@ public class BookStoreHttpApiClientModule : AbpModule
`AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation.
Now you're ready to generate the client proxy code by running the following command in the root folder of your client project when your project is running.
Now you're ready to generate the client proxy code by running the following command in the root folder of your client project while your server-side project is running:
You should have seen the generated files under the selected folder.
<br/>
> The URL (`-u` parameter's value) might be different for your application. It should be server's root URL.
You should have seen the generated files under the selected folder:

Now you can run your test console application and you should see the same output.
> Books: Anna Karenina, Crime and Punishment, Mother
Now you can run the console client application again. You should see the same output:
### Add authorization
ABP Framework provides an authorization system based on the ASP.NET Core's authorization infrastructure.
Even so, to use that need to make some configurations.
````
Books: Anna Karenina, Crime and Punishment, Mother
````
Under `Acme.BookStore.Application.Contracts` open `BookStorePermissions` and paste the below code
## Add authorization
ABP Framework provides an [authorization system](https://docs.abp.io/en/abp/latest/Authorization) based on the [ASP.NET Core's authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). We can define permissions and restrict access to some of our application's functionalities, so only the allowed users/clients can use these functionalities. Here, I will define a permission to be able to get the list of books.
Under `Acme.BookStore.Application.Contracts` open `BookStorePermissions` and paste the below code:
```csharp
namespace Acme.BookStore.Permissions;
@ -214,31 +200,37 @@ public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvide
{
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore"));
After completing that you can make the localization configuration and you should give permission at the Admin UI side. You can see the same output again and all will be alright.
To fix the problem, we should grant permission for the admin user. We are granting permission to the admin user because the console application is configured to use Resource Owner Password Grant Flow. That means the client application is consuming services on behalf of the admin user. You can see the configuration in the `appsettings.json` file of the console application.
### Further Reading
In this small tutorial, I explained how you can create an example project and apply static client proxy instead of dynamic client proxy. Also summarized the differences between both approaches.
**TODO: Show how to give permission, with screenshots**
If you want to get more information, you can read the following documents:
### Further Reading
In this tutorial, I explained how you can create an example project and apply static client proxy instead of dynamic client proxy. Also summarized the differences between both approaches. If you want to get more information, you can read the following documents:
* [Static C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Static-CSharp-API-Clients)
* [Dynamic C# API Client Proxies](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients)
* [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)
* [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)