ASP.NET Boilerplate项目按模块生成独立NSwag服务代理的问询
按模块生成NSwag服务代理的解决方案
我之前在基于ASP.NET Boilerplate v2.0.1 + .NET Core + Angular 2的项目里,遇到过完全一样的问题——共用API端点导致生成的代理包含所有模块的服务,既臃肿又不利于模块解耦。这里分享几个亲测有效的方案:
方案一:基于路由前缀的NSwag过滤(最快捷)
这是最简单的实现方式,核心思路是给每个模块的API设置统一的路由前缀,然后在NSwag配置里过滤出对应前缀的接口生成代理:
- 后端配置路由前缀:给每个模块的控制器添加专属路由前缀,比如模块1的控制器都用
/api/module1/开头:
[Route("api/module1/[controller]")] public class Module1Controller : AbpController { // 模块1的接口方法 }
- 创建模块专属NSwag配置文件:在Angular项目根目录下,复制原有的
nswag.json,命名为nswag-module1.json,修改其中的operationProcessors节点,添加路由过滤规则:
"operationProcessors": [ { "type": "OperationFilterProcessor", "excludeParametersWithoutDescription": false, "filter": "operation => operation.path.startsWith('/api/module1/')" } ]
同时记得修改output节点,把生成的代理文件输出到对应模块的目录,比如src/app/module1/services/module1-proxy.ts。
- 执行模块代理生成命令:在终端运行对应模块的生成命令:
nswag run nswag-module1.json
同理,为其他模块创建对应的配置文件和生成命令即可。
方案二:基于自定义特性的接口过滤(更灵活)
如果你的模块路由没有统一前缀,或者希望更精准地控制哪些接口属于哪个模块,可以用自定义特性标记控制器,再通过NSwag过滤:
- 后端定义模块特性:创建一个自定义特性,用来标记接口所属的模块:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class ModuleApiAttribute : Attribute { public string ModuleName { get; } public ModuleApiAttribute(string moduleName) => ModuleName = moduleName; }
- 给控制器打标记:在对应模块的控制器上添加这个特性:
[ModuleApi("Module1")] [Route("api/some-custom-path/[controller]")] public class Module1Controller : AbpController { // ... }
- NSwag配置特性过滤:修改模块专属的NSwag配置文件,添加基于特性的过滤规则:
"operationProcessors": [ { "type": "OperationFilterProcessor", "filter": "operation => operation.operationDescription?.methodInfo?.DeclaringType?.GetCustomAttributes(true).Any(a => a is YourProjectNamespace.ModuleApiAttribute attr && attr.ModuleName == 'Module1') == true" } ]
这里需要替换YourProjectNamespace为你后端项目的实际命名空间。
方案三:提前铺垫独立API端点(适配未来规划)
既然你未来计划给单个模块配置独立API端点,现在可以提前按模块拆分路由,为后续的端点独立做铺垫:
在后端Startup.cs的UseEndpoints方法里,为每个模块配置独立的路由组:
app.UseEndpoints(endpoints => { // 模块1的路由组 endpoints.MapControllers() .RequireAuthorization() .AddApplicationPart(typeof(Module1Controller).Assembly) .MapAreaControllerRoute( name: "module1", areaName: "Module1", pattern: "api/module1/{controller=Home}/{action=Index}/{id?}"); // 模块2的路由组 endpoints.MapControllers() .RequireAuthorization() .AddApplicationPart(typeof(Module2Controller).Assembly) .MapAreaControllerRoute( name: "module2", areaName: "Module2", pattern: "api/module2/{controller=Home}/{action=Index}/{id?}"); });
之后同样用方案一的路由前缀过滤方式生成模块代理,等未来要切换独立端点时,只需要修改NSwag配置里的swaggerGenerator的url为对应模块的独立API地址即可,几乎不需要改动代理使用的代码。
额外注意事项
- 生成的代理文件要放在对应模块的目录下,配合Angular的懒加载模块,能更好地实现模块的独立打包和部署;
- 可以在
package.json里添加模块代理生成的脚本,比如"generate:module1": "nswag run nswag-module1.json",方便快速执行; - 每次后端API更新后,记得重新生成对应模块的代理文件,避免前端调用出错。
内容的提问来源于stack exchange,提问作者Satyanarayan Bajoria




