服务代理
从 Angular 应用调用 REST 端点是很常见的需求。我们通常会创建与服务器端控制器对应的服务,以及与 DTO 对应的接口来与服务器交互。这往往需要手动将C#代码转换为TypeScript版本,即使不算无法容忍,也相当令人困扰。
为避免手动操作,我们可能会使用类似 NSWAG 的工具来生成服务代理。但NSWAG存在一些缺点:
- 它会生成单个.ts文件,随着应用增长文件会过于庞大。而且这种单文件模式不符合ABP的**模块化架构**
- 坦率地说,生成的代码有些不够优雅。我们希望生成的代码看起来像是人工编写的
- 由于swagger.json无法准确反映后端服务的方法签名,NSWAG也无法在客户端准确还原这些签名
ABP提供了一个能暴露服务端方法契约的端点。当运行generate-proxy命令时,ABP CLI会向该端点发起HTTP请求,并生成更匹配的TypeScript客户端代理。它会按命名空间组织文件夹结构,添加桶状导出,并在Angular服务中准确反映方法签名。
开始前请确保通过
dotnet run启动后端应用。Visual Studio存在已知限制,请勿使用其内置Web服务器运行项目。
在Angular应用的根目录运行以下命令:
abp generate-proxy -t ng
注意
- 若使用NX,请注意基于Angular原理图的ABP包可能无法正常工作。我们为NX代码库提供了专用包@abp/nx.generators,建议使用该生成器。具体说明请参考相关章节
- 不带参数的命令仅会为当前应用服务生成代理,并放置在默认Angular应用中。可通过多个参数调整此行为,详见详情
生成的文件将位于目标项目根目录的proxy文件夹内。
每个文件夹将包含对应命名空间中定义的模型、枚举和服务,并配有桶状导出文件(即index.ts)以简化导入。
命令通过读取
angular.json文件识别应用/库根目录。请确保已将目标项目设为defaultProject或通过--target参数指定。这也意味着您可以使用monorepo工作区。
Angular项目配置
若项目创建版本为3.1或更高,可跳过此部分,相关配置已预置。
针对v3.1之前创建的解决方案,按以下步骤配置Angular应用:
- 在Angular项目的
devDependencies中添加@abp/ng.schematics包。在Angular应用根目录运行:
npm install @abp/ng.schematics -D
- 在应用项目的
/src/environments/environment.ts中添加rootNamespace属性(需将MyCompanyName.MyProjectName替换为.NET项目的根命名空间):
export const environment: Config.Environment = {
// 其他环境变量...
apis: {
default: {
rootNamespace: "MyCompanyName.MyProjectName",
// 其他环境变量...
},
},
};
- [可选] 在
tsconfig.base.json中添加以下路径配置,方便导入代理:
{
// 其他TS配置...
"compilerOptions": {
// 其他TS配置...
"paths": {
"@proxy": ["src/app/proxy/index.ts"],
"@proxy/*": ["src/app/proxy/*"]
}
}
}
proxy文件夹的生成位置和上述路径可能因项目结构而异。
generate-proxy参数说明
- module或-m: 后端模块名称。默认为
app。对应api/abp/api-definition响应中定义模块的对象键。例如要为PermissionManagement生成代理,应传入permissionManagement - apiName或-a: 后端API名称(即remoteServiceName)。在选定模块中定义(位于
api/abp/api-definition响应)。属性键名为remoteServiceName。例如PermissionManagement应传入AbpPermissionManagement - source: 用于解析API定义URL和根命名空间的Angular项目源
- target: 放置生成代码的Angular项目目标。例如设为
permission-management时,路径将类似(npm/ng-packs/packages/permission-management) - entryPoint: 在目标项目中创建生成代理的目录。目录结构为
permission-management/proxy/src/lib/proxy,其中permission-management是target值。若想为生成代理创建文件夹,有两种选择:将entryPoint设为proxy,或在project.json中将sourceRoot从packages/permission-management/src改为packages/permission-management/proxy/src - serviceType: 生成代理的服务类型。可选值为
application、integration和all。默认值为application。开发人员可将服务标记为“集成服务”,若需跳过该服务的代理生成,此设置非常有用。详见集成服务
服务
generate-proxy命令会为每个后端控制器生成对应服务,并为控制器中的每个操作生成方法(实际为函数值的属性)。这些方法通过 RestService 调用后端 API。
每个服务中定义了apiName变量(v2.4+支持)。apiName与模块的RemoteServiceName匹配。该变量会在每个请求中作为参数传递给RestService。若环境中未定义微服务API,RestService将使用默认配置。参见 从应用配置获取特定API端点
服务的providedIn属性设置为'root',因此无需在模块中单独提供。可直接注入使用:
import { BookService } from '@proxy/books';
import { inject } from '@angular/core';
@Component(/* 组件元数据 */)
export class BookComponent implements OnInit {
private service = inject(BookService);
ngOnInit() {
this.service.get().subscribe(
// 对响应进行处理
);
}
}
Angular编译器会从最终输出中移除未被注入的服务。参见可摇树优化提供者文档。
模型
generate-proxy命令会生成与后端DTO匹配的接口。@abp/ng.core包中也包含一些核心DTO。这些模型可共同用于反映API结构。
import { PagedResultDto } from "@abp/ng.core";
import { BookDto } from "@proxy/books";
@Component(/* 组件元数据 */)
export class BookComponent implements OnInit {
data: PagedResultDto<BookDto> = {
items: [],
totalCount: 0,
};
}
枚举
枚举在前端一直难以处理。generate-proxy命令会在单独文件中生成枚举,并从同一文件导出开箱即用的“选项常量”。可如下导入:
import { bookGenreOptions } from "@proxy/books";
@Component(/* 组件元数据 */)
export class BookComponent implements OnInit {
genres = bookGenreOptions;
}
...并在模板中使用这些选项:
<!-- 为简洁起见已简化 -->
<select formControlName="genre">
<option [ngValue]="null">选择类型</option>
<option *ngFor="let genre of genres" [ngValue]="genre.value">
{{ genre.key }}
</option>
</select>
请 参阅本文 深入了解服务代理。
ABP NX代理生成器
对于使用NX的项目,@abp/nx.generators包提供无缝集成。该包本质上是为NX代码库量身定制的封装器。
安装 通过以下命令将包添加到项目:
yarn add @abp/nx.generators
使用 运行以下命令使用生成器:
yarn nx generate @abp/nx.generators:generate-proxy
// 或
yarn nx g @abp/nx.generators:generate-proxy
注意: 该生成器参数与标准ABP代理生成器保持一致。
已知限制
当使用Visual Studio通过IIS Express作为Web服务器运行项目时,端点将无法远程访问。这是IIS Express的默认行为,旨在防范网络运行的安全风险。但这会导致代理生成器失败,因为它需要从/api/abp/api-definition端点获取响应。可通过Kestrel提供服务来避免此问题。在项目文件夹中运行dotnet run即可实现。
抠丁客



