Skip to content

Commit 80a3d3c

Browse files
committed
Merge branch 'dev' of https://github.com/abpframework/abp into dev
2 parents af000d1 + 9988341 commit 80a3d3c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1302
-1133
lines changed

docs/en/Tutorials/Part-1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ $(function () {
727727

728728
* `abp.libs.datatables.createAjax` is a helper function to adapt ABP's dynamic JavaScript API proxies to [Datatable](https://datatables.net/)'s format.
729729
* `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirement to use it, but it simplifies the [Datatables](https://datatables.net/) configuration by providing conventional values for missing options.
730-
* `acme.bookStore.book.getList` is the function to get list of books (as described in [dynamic JavaScript proxies](#Dynamic JavaScript proxies)).
730+
* `acme.bookStore.book.getList` is the function to get list of books (as described in [dynamic JavaScript proxies](#dynamic-javascript-proxies)).
731731
* See [Datatables documentation](https://datatables.net/manual/) for all configuration options.
732732

733733
It's end of this part. The final UI of this work is shown as below:

docs/en/UI/Angular/Component-Replacement.md

+19-15
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,22 @@ export class AppComponent {
3131

3232
## Available Replaceable Components
3333

34-
| Component key | Description |
35-
|----------------------------------------------------|-------------------------|
36-
| Account.LoginComponent | Login page |
37-
| Account.RegisterComponent | Register page |
38-
| Account.ManageProfileComponent | Manage Profile page |
39-
| Account.AuthWrapperComponent | This component wraps register and login pages |
40-
| Account.ChangePasswordComponent | Change password form |
41-
| Account.PersonalSettingsComponent | Personal settings form |
42-
| Account.TenantBoxComponentInputs | Tenant changing box |
43-
| FeatureManagement.FeatureManagementComponent | Features modal |
44-
| Identity.UsersComponent | Users page |
45-
| Identity.RolesComponent | Roles page |
46-
| PermissionManagement.PermissionManagementComponent | Permissions modal |
47-
| SettingManagement.SettingManagementComponent | Setting Management page |
48-
| TenantManagement.TenantsComponent | Tenants page |
34+
| Component key | Description |
35+
| -------------------------------------------------- | --------------------------------------------- |
36+
| Account.LoginComponent | Login page |
37+
| Account.RegisterComponent | Register page |
38+
| Account.ManageProfileComponent | Manage Profile page |
39+
| Account.AuthWrapperComponent | This component wraps register and login pages |
40+
| Account.ChangePasswordComponent | Change password form |
41+
| Account.PersonalSettingsComponent | Personal settings form |
42+
| Account.TenantBoxComponentInputs | Tenant changing box |
43+
| FeatureManagement.FeatureManagementComponent | Features modal |
44+
| Identity.UsersComponent | Users page |
45+
| Identity.RolesComponent | Roles page |
46+
| PermissionManagement.PermissionManagementComponent | Permissions modal |
47+
| SettingManagement.SettingManagementComponent | Setting Management page |
48+
| TenantManagement.TenantsComponent | Tenants page |
49+
50+
## What's Next?
51+
52+
- [Custom Setting Page](./Custom-Setting-Page.md)

docs/en/UI/Angular/AddingSettingTab.md docs/en/UI/Angular/Custom-Setting-Page.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Creating a Settings Tab
1+
# Custom Setting Page
22

33
There are several settings tabs from different modules. You can add custom settings tabs to your project in 3 steps.
44

@@ -11,12 +11,11 @@ import { Component } from '@angular/core';
1111
@Component({
1212
selector: 'app-your-custom-settings',
1313
template: `
14-
your-custom-settings works! mySetting: {%{{{ mySetting$ | async }}}%}
14+
custom-settings works!
1515
`,
1616
})
1717
export class YourCustomSettingsComponent {
18-
@Select(ConfigState.getSetting('MyProjectName.MySetting1')) // Gets a setting. MyProjectName.MySetting1 is a setting key.
19-
mySetting$: Observable<string>; // The selected setting is set to the mySetting variable as Observable.
18+
// Your component logic
2019
}
2120
```
2221

-21.8 KB
Loading

docs/en/docs-nav.json

+4
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@
328328
{
329329
"text": "Component Replacement",
330330
"path": "UI/Angular/Component-Replacement.md"
331+
},
332+
{
333+
"text": "Custom Setting Page",
334+
"path": "UI/Angular/Custom-Setting-Page.md"
331335
}
332336
]
333337
}
+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# 自动API控制器
2+
3+
创建[应用程序服务](Application-Services.md)后, 通常需要创建API控制器以将此服务公开为HTTP(REST)API端点. 典型的API控制器除了将方法调用重定向到应用程序服务并使用[HttpGet],[HttpPost],[Route]等属性配置REST API之外什么都不做.
4+
5+
ABP可以按照惯例 **自动** 将你的应用程序服务配置为API控制器. 大多数时候你不关心它的详细配置,但它可以完全被自定义.
6+
7+
## 配置
8+
9+
基本配置很简单. 只需配置`AbpAspNetCoreMvcOptions`并使用`ConventionalControllers.Create`方法,如下所示:
10+
11+
````csharp
12+
[DependsOn(BookStoreApplicationModule)]
13+
public class BookStoreWebModule : AbpModule
14+
{
15+
public override void ConfigureServices(ServiceConfigurationContext context)
16+
{
17+
Configure<AbpAspNetCoreMvcOptions>(options =>
18+
{
19+
options
20+
.ConventionalControllers
21+
.Create(typeof(BookStoreApplicationModule).Assembly);
22+
});
23+
}
24+
}
25+
````
26+
27+
此示例代码配置包含类`BookStoreApplicationModule`的程序集中的所有应用程序服务.下图显示了[Swagger UI](https://swagger.io/tools/swagger-ui/)上的API内容.
28+
29+
![bookstore-apis](../images/bookstore-apis.png)
30+
31+
### 例子
32+
33+
一些示例方法名称和按约定生成的相应路由:
34+
35+
| 服务方法名称 | HTTP Method | 路由 |
36+
| ----------------------------------------------------- | ----------- | -------------------------- |
37+
| GetAsync(Guid id) | GET | /api/app/book/{id} |
38+
| GetListAsync() | GET | /api/app/book |
39+
| CreateAsync(CreateBookDto input) | POST | /api/app/book |
40+
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} |
41+
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} |
42+
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors |
43+
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor |
44+
45+
### HTTP Method
46+
47+
ABP在确定服务方法的HTTP Method时使用命名约定:
48+
49+
- **Get**: 如果方法名称以`GetList`,`GetAll``Get`开头.
50+
- **Put**: 如果方法名称以`Put``Update`开头.
51+
- **Delete**: 如果方法名称以`Delete``Remove`开头.
52+
- **Post**: 如果方法名称以`Create`,`Add`,`Insert``Post`开头.
53+
- **Patch**: 如果方法名称以`Patch`开头.
54+
- 其他情况, **Post****默认方式**.
55+
56+
如果需要为特定方法自定义HTTP Method, 则可以使用标准ASP.NET Core的属性([HttpPost], [HttpGet], [HttpPut]... 等等.). 这需要添加[Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core)的Nuget包.
57+
58+
### 路由
59+
60+
路由根据一些惯例生成:
61+
62+
* 它始终以 **/api**开头.
63+
* 接着是**路由路径**. 默认值为"**/app**", 可以进行如下配置:
64+
65+
````csharp
66+
Configure<AbpAspNetCoreMvcOptions>(options =>
67+
{
68+
options.ConventionalControllers
69+
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
70+
{
71+
opts.RootPath = "volosoft/book-store";
72+
});
73+
});
74+
````
75+
76+
然后获得一本书的路由将是'**/api/volosoft/book-store/book/{id}**'. 此示例使用两级根路径,但通常使用单个级别的深度.
77+
78+
* 接着 **标准化控制器/服务名称**. 会删除`AppService`,`ApplicationService``Service`的后缀并将其转换为 **camelCase**. 如果你的应用程序服务类名称为`BookAppService`.那么它将变为`/book`.
79+
* 如果要自定义命名, 则设置`UrlControllerNameNormalizer`选项. 它是一个委托允许你自定义每个控制器/服务的名称.
80+
* 如果该方法具有 '**id**'参数, 则会在路由中添加'**/{id}**'.
81+
* 如有必要,它会添加操作名称. 操作名称从服务上的方法名称获取并标准化;
82+
* 删除'**Async**'后缀. 如果方法名称为'GetPhonesAsync',则变为`GetPhones`.
83+
* 删除**HTTP method前缀**. 基于的HTTP method删除`GetList`,`GetAll`,`Get`,`Put`,`Update`,`Delete`,`Remove`,`Create`,`Add`,`Insert`,`Post``Patch`前缀, 因此`GetPhones`变为`Phones`, 因为`Get`前缀和GET请求重复.
84+
* 将结果转换为**camelCase**.
85+
* 如果生成的操作名称为****,则它不会添加到路径中.否则它会被添加到路由中(例如'/phones').对于`GetAllAsync`方法名称,它将为空,因为`GetPhonesAsync`方法名称将为`phone`.
86+
* 可以通过设置`UrlActionNameNormalizer`选项来自定义.It's an action delegate that is called for every method.
87+
* 如果有另一个带有'Id'后缀的参数,那么它也会作为最终路线段添加到路线中(例如'/phoneId').
88+
89+
## 服务选择
90+
91+
创建的HTTP API控制器并不是应用服务所独有的功能.
92+
93+
### IRemoteService 接口
94+
95+
如果一个类实现了`IRemoteService`接口, 那么它会被自动选择为API控制器. 由于应用程序服务本身实现了`IRemoteService`接口, 因此它自然就成为API控制器.
96+
97+
### RemoteService Attribute
98+
99+
`RemoteService`可用于将实现`IRemoteService`接口的类标记为远程服务或禁用它. 例如:
100+
101+
````csharp
102+
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)]
103+
public class PersonAppService : ApplicationService
104+
{
105+
106+
}
107+
````
108+
109+
### TypePredicate 选项
110+
111+
你可以通过提供`TypePedicate`选项进一步过滤类以成为API控制器:
112+
113+
````csharp
114+
services.Configure<AbpAspNetCoreMvcOptions>(options =>
115+
{
116+
options.ConventionalControllers
117+
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
118+
{
119+
opts.TypePredicate = type => { return true; };
120+
});
121+
});
122+
````
123+
124+
如果你不想将此类型公开为API控制器, 则可以在类型检查时返回`false`.
125+
126+
## API Explorer
127+
128+
API Explorer是可以由客户端获取API结构的服务. Swagger使用它为endpoint创建文档和test UI.
129+
130+
默认情况下, HTTP API控制器会自动启用API Explorer, 可以使用`RemoteService`按类或方法的级别控制它. 例如:
131+
132+
````csharp
133+
[RemoteService(IsMetadataEnabled = false)]
134+
public class PersonAppService : ApplicationService
135+
{
136+
137+
}
138+
````
139+
140+
禁用`IsMetadataEnabled`从而从API Explorer中隐藏此服务, 并且无法被发现. 但是它仍然可以被知道确切API路径/路由的客户端使用.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# 动态 C# API 客户端
2+
3+
ABP可以自动创建C# API 客户端代理来调用远程HTTP服务(REST APIS).通过这种方式,你不需要通过 `HttpClient` 或者其他低级的HTTP功能调用远程服务并获取数据.
4+
5+
## 服务接口
6+
7+
你的service或controller需要实现一个在服务端和客户端共享的接口.因此,首先需要在一个共享的类库项目中定义一个服务接口.例如:
8+
9+
````csharp
10+
public interface IBookAppService : IApplicationService
11+
{
12+
Task<List<BookDto>> GetListAsync();
13+
}
14+
````
15+
16+
为了能自动被发现,你的接口需要实现`IRemoteService`接口.由于`IApplicationService`继承自`IRemoteService`接口.所以`IBookAppService`完全满足这个条件.
17+
18+
在你的服务中实现这个类,你可以使用[auto API controller system](Auto-API-Controllers.md)将你的服务暴漏为一个REST API 端点.
19+
20+
## 客户端代理生成
21+
22+
首先,将[Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget包添加到你的客户端项目中:
23+
24+
````
25+
Install-Package Volo.Abp.Http.Client
26+
````
27+
28+
然后给你的模块添加`AbpHttpClientModule`依赖:
29+
30+
````csharp
31+
[DependsOn(typeof(AbpHttpClientModule))] //添加依赖
32+
public class MyClientAppModule : AbpModule
33+
{
34+
}
35+
````
36+
37+
现在,已经可以创建客户端代理了.例如:
38+
39+
````csharp
40+
[DependsOn(
41+
typeof(AbpHttpClientModule), //用来创建客户端代理
42+
typeof(BookStoreApplicationModule) //包含应用服务接口
43+
)]
44+
public class MyClientAppModule : AbpModule
45+
{
46+
public override void ConfigureServices(ServiceConfigurationContext context)
47+
{
48+
//创建动态客户端代理
49+
context.Services.AddHttpClientProxies(
50+
typeof(BookStoreApplicationModule).Assembly
51+
);
52+
}
53+
}
54+
````
55+
56+
`AddHttpClientproxies`方法获得一个程序集,找到这个程序集中所有的服务接口,创建并注册代理类.
57+
58+
### Endpoint配置
59+
60+
`appsettings.json`文件中的`RemoteServices`节点被用来设置默认的服务地址.下面是最简单的配置:
61+
62+
````
63+
{
64+
"RemoteServices": {
65+
"Default": {
66+
"BaseUrl": "http://localhost:53929/"
67+
}
68+
}
69+
}
70+
````
71+
72+
查看下面的"RemoteServiceOptions"章节获取更多详细配置.
73+
74+
## 使用
75+
76+
可以很直接地使用.只需要在你的客户端程序中注入服务接口:
77+
78+
````csharp
79+
public class MyService : ITransientDependency
80+
{
81+
private readonly IBookAppService _bookService;
82+
83+
public MyService(IBookAppService bookService)
84+
{
85+
_bookService = bookService;
86+
}
87+
88+
public async Task DoIt()
89+
{
90+
var books = await _bookService.GetListAsync();
91+
foreach (var book in books)
92+
{
93+
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
94+
}
95+
}
96+
}
97+
````
98+
99+
本例注入了上面定义的`IBookAppService`服务接口.当客户端调用服务方法的时候动态客户端代理就会创建一个HTTP调用.
100+
101+
### IHttpClientProxy接口
102+
103+
你可以像上面那样注入`IBookAppService`来使用客户端代理,也可以注入`IHttpClientProxy<IBookAppService>`获取更多明确的用法.这种情况下你可以使用`IHttpClientProxy<T>`接口的`Service`属性.
104+
105+
## 配置
106+
107+
### RemoteServiceOptions
108+
109+
默认情况下`AbpRemoteServiceOptions``appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如:
110+
111+
````csharp
112+
public override void ConfigureServices(ServiceConfigurationContext context)
113+
{
114+
context.Services.Configure<AbpRemoteServiceOptions>(options =>
115+
{
116+
options.RemoteServices.Default =
117+
new RemoteServiceConfiguration("http://localhost:53929/");
118+
});
119+
120+
//...
121+
}
122+
````
123+
124+
### 多个远程服务端点
125+
126+
上面的例子已经配置了"Default"远程服务端点.你可能需要为不同的服务创建不同的端点.(就像在微服务方法中一样,每个微服务具有不同的端点).在这种情况下,你可以在你的配置文件中添加其他的端点:
127+
128+
````json
129+
{
130+
"RemoteServices": {
131+
"Default": {
132+
"BaseUrl": "http://localhost:53929/"
133+
},
134+
"BookStore": {
135+
"BaseUrl": "http://localhost:48392/"
136+
}
137+
}
138+
}
139+
````
140+
141+
`AddHttpClientProxies`方法有一个可选的参数来定义远程服务的名字:
142+
143+
````csharp
144+
context.Services.AddHttpClientProxies(
145+
typeof(BookStoreApplicationModule).Assembly,
146+
remoteServiceName: "BookStore"
147+
);
148+
````
149+
150+
`remoteServiceName`参数会匹配通过`AbpRemoteServiceOptions`配置的服务端点.如果`BookStore`端点没有定义就会使用默认的`Default`端点.
151+
152+
### 作为默认服务
153+
154+
当你为`IBookAppService`创建了一个服务代理,你可以直接注入`IBookAppService`来使用代理客户端(像上面章节中将的那样).你可以传递`asDefaultService:false``AddHttpClientProxies`方法来禁用此功能.
155+
156+
````csharp
157+
context.Services.AddHttpClientProxies(
158+
typeof(BookStoreApplicationModule).Assembly,
159+
asDefaultServices: false
160+
);
161+
````
162+
163+
如果你的程序中已经有一个服务的实现并且你不想用你的客户端代理重写或替换其他的实现,就需要使用`asDefaultServices:false`
164+
165+
> 如果你禁用了`asDefaultService`,你只能使用`IHttpClientProxy<T>`接口去使用客户端代理.(参见上面的相关章节).
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# abp.auth JavaScript API
2+
3+
TODO

0 commit comments

Comments
 (0)