mirror of https://github.com/abpframework/abp.git
4 changed files with 288 additions and 0 deletions
@ -0,0 +1,288 @@ |
|||
# 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 using static client proxy by creating a new project and converting that from dynamic client proxy to static client proxy. Also, I will glance at the differences 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. |
|||
|
|||
````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). |
|||
|
|||
At the same folder build the project with the following command on the cli. |
|||
````shell |
|||
dotnet build /graphbuild |
|||
```` |
|||
|
|||
It will restore the project and download the NuGet packages. |
|||
|
|||
Now you should run the DbMigrator project to up your database. |
|||
|
|||
Now your project is ready you can run it properly. |
|||
|
|||
 |
|||
|
|||
From now on, we will add some files to show the case to you. |
|||
|
|||
|
|||
### Create application service interface |
|||
You should open your web application then find `Pages` folder and a create new folder named `Books`. |
|||
You should create a new razor page and a new js file as named index. |
|||
|
|||
Change the Pages/Books/Index.cshtml as the following: |
|||
```csharp |
|||
@page |
|||
@using Acme.BookStore.Localization |
|||
@using Acme.BookStore.Web.Pages.Books |
|||
@using Microsoft.Extensions.Localization |
|||
@model IndexModel |
|||
@inject IStringLocalizer<BookStoreResource> L |
|||
@section scripts |
|||
{ |
|||
<abp-script src="/Pages/Books/index.js" /> |
|||
} |
|||
<abp-card> |
|||
<abp-card-header> |
|||
<h2>@L["Books"]</h2> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-table striped-rows="true" id="BooksTable"></abp-table> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
``` |
|||
|
|||
Now change index.js file as the following content |
|||
```js |
|||
$(function () { |
|||
var l = abp.localization.getResource('BookStore'); |
|||
$('#BooksTable').DataTable( |
|||
abp.libs.datatables.normalizeConfiguration({ |
|||
serverSide: true, |
|||
paging: true, |
|||
order: [[1, "asc"]], |
|||
searching: false, |
|||
scrollX: true, |
|||
ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList), |
|||
columnDefs: [ |
|||
{ |
|||
title: l('Name'), |
|||
data: "name" |
|||
}, |
|||
{ |
|||
title: l('AuthorName'), |
|||
data: "authorName" |
|||
}, |
|||
{ |
|||
title: l('Price'), |
|||
data: "price" |
|||
} |
|||
] |
|||
}) |
|||
); |
|||
}); |
|||
``` |
|||
|
|||
# Implement the application service |
|||
Assume that we have an `IBookAppService` interface: |
|||
|
|||
````csharp |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<List<BookDto>> GetListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
That uses a `BookDto` defined as shown: |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public class BookDto : AuditedEntityDto<Guid> |
|||
{ |
|||
public string AuthorName { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public DateTime PublishDate { get; set; } |
|||
|
|||
public float Price { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```csharp |
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public class BookAppService : ApplicationService, IBookAppService |
|||
{ |
|||
public Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input) |
|||
{ |
|||
var bookDtos = new List<BookDto>() |
|||
{ |
|||
new BookDto(){ Name = "Anna Karenina", AuthorName ="Tolstoy", Price = 50}, |
|||
new BookDto(){ Name = "Crime and Punishment", AuthorName ="Dostoevsky", Price = 60}, |
|||
new BookDto(){ Name = "Mother", AuthorName ="Gorki", Price = 70} |
|||
}; |
|||
return Task.FromResult(new PagedResultDto<BookDto>( |
|||
bookDtos.Count, |
|||
bookDtos |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
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) |
|||
|
|||
### Consume the app service from the console application |
|||
Add a new test class, named BookAppService_Tests in the Application.Tests |
|||
|
|||
```csharp |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Acme.BookStore.Books |
|||
{ |
|||
public class BookAppService_Tests : BookStoreApplicationTestBase |
|||
{ |
|||
private readonly IBookAppService _bookAppService; |
|||
|
|||
public BookAppService_Tests() |
|||
{ |
|||
_bookAppService = GetRequiredService<IBookAppService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_List_Of_Books() |
|||
{ |
|||
var result = await _bookAppService.GetListAsync(); |
|||
Assert.Equal(3, result.Count); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Convert application to use static client proxies |
|||
Before showing you how to use static client proxies instead of dynamic client proxy, I ask for talk differences between both approach. Their similarty, advantages and disadvantages to each other. |
|||
|
|||
##### Benefits |
|||
* 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. |
|||
|
|||
##### 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 working on the runtime can take more a bit of time. |
|||
|
|||
First, add Volo.Abp.Http.Client NuGet package to your client project: |
|||
````shell |
|||
Install-Package Volo.Abp.Http.Client |
|||
```` |
|||
Then add AbpHttpClientModule dependency to your module: |
|||
```csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule) |
|||
//the other dependencies |
|||
)] |
|||
|
|||
public class BookStoreApplicationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//Other configurations |
|||
|
|||
// Prepare for static client proxy generation |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly |
|||
); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
`AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation. |
|||
|
|||
> 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. |
|||
|
|||
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. |
|||
|
|||
````bash |
|||
abp generate-proxy -t csharp -u http://localhost:44397/ |
|||
```` |
|||
|
|||
Also, you should then run the below command under your web project for the UI side for MVC |
|||
````bash |
|||
abp generate-proxy -t js -u http://localhost:44397/ |
|||
```` |
|||
|
|||
You have been should the generated files under the runned folder. |
|||
|
|||
### 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. |
|||
|
|||
Under `Acme.BookStore.Application.Contracts` open `BookStorePermissions` and paste the below code |
|||
```csharp |
|||
public static class BookStorePermissions |
|||
{ |
|||
public const string GroupName = "BookStore"; |
|||
|
|||
public static class Books |
|||
{ |
|||
public const string Default = GroupName + ".Books"; |
|||
} |
|||
|
|||
} |
|||
``` |
|||
Also need to change `BookStorePermissionDefinitionProvider` under the same folder and project as following. |
|||
```csharp |
|||
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var bookStoreGroup = context.AddGroup(BookStorePermissions.GroupName, L("Permission:BookStore")); |
|||
bookStoreGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books")); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<BookStoreResource>(name); |
|||
} |
|||
} |
|||
``` |
|||
And now you should add [Authorize(BookStorePermissions.Books.Default)] to `BookAppService` |
|||
If you don't give permission you should see the following screen. |
|||
 |
|||
|
|||
After completing that you can make localization configuration and you should give permission from the Admin UI side. Now you should the following screen. |
|||
 |
|||
|
|||
### 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. |
|||
|
|||
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) |
|||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 114 KiB |
Loading…
Reference in new issue