DI NSwag 自动生成客户端的正确方法
Proper way to DI NSwag auto-generated client
将 VS 连接服务 (NSwag) 注入 classes/controllers 的首选方法是什么。我在网上找到了很多使用这种形式的建议:
services.AddHttpClient<IClient, Client>((provider, client) =>
{
client.BaseAddress = new System.Uri("https://some.baseurl/");
});
但是这会导致错误
{"errorMessage":"Unable to resolve service for type 'System.String' while attempting to activate 'xxx.Client'."}
这来自 obj
中自动生成的客户端 class,它似乎在构造函数中强制使用字符串 BaseUrl,当然 DI 无法解析:
public Client(string baseUrl, System.Net.Http.HttpClient httpClient)
{
BaseUrl = baseUrl;
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
这个基础 URL 后来被强制进入 url 构建器代码,所以它不能真正被绕过。然而,即使是网络上对客户端 classes 使用部分扩展的解决方案似乎也完全忽略了 auto-gen class(如 here)中的 baseUrl。就好像它不存在一样(这很奇怪,NSwag 在生成不同的构造函数之前有吗?)。 class 是通过 csproj 生成的:
<ItemGroup>
<OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="xxx" ClassName="Client">
<SourceUri>https://localhost:44353/swagger/v1/swagger.json</SourceUri>
</OpenApiReference>
</ItemGroup>
这导致目标构建调用:
2>GenerateNSwagCSharp:
2> "C:\.<path>./tools/Win/NSwag.exe" openapi2csclient /className:Client /namespace:xxx /input:"C:\<projpath>\OpenAPIs\swagger.json" /output:"obj\swaggerClient.cs"
2>NSwag command line tool for .NET 4.6.1+ WinX64, toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))
那么,这是怎么做到的?潜在地,在不为代理 class 创建另一个代理 class 的情况下,我宁愿 DI 处理我的对象生命周期。如果可能的话,我也想避免使用 NSwagStudio,并希望保留 VS 提供的工具。
好吧,其实我是通过OpenApiReference
摸索着解决了这个问题,但是需要手动修改csproj文件。必须将额外的 Options
节点添加到 OpenApiReference
项目组,以指示 NSwag 不公开 BaseUrl 并生成一个接口,这可以简化设置 DI 的工作,而无需额外的代码。
Visual Studio 团队真的应该将这两个复选框添加到 OpenAPI 的连接服务 screens/configuration。
<ItemGroup>
<OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="xxx" ClassName="Client">
<SourceUri>https://localhost:44353/swagger/v1/swagger.json</SourceUri>
<Options>/UseBaseUrl:false /GenerateClientInterfaces:true</Options>
</OpenApiReference>
</ItemGroup>
现在只有一个 HttpClient
构造函数,NSwag 客户端代理使用它的基地址,因此 AddHttpClient
通过 DI 可以正常工作。
由于生成的 class 被标记为部分,您可以提供一个额外的构造函数并用 [ActivatorUtilitiesConstructor]
属性标记它。应用该属性可确保构造函数与依赖注入一起使用。您还可以在部分扩展中实现该接口。这是一个例子;
public partial class MyApiClient : IMyApiClient
{
[ActivatorUtilitiesConstructor] // This ctor will be used by DI
public MyApiClient(HttpClient httpClient, IOptions<MyApiClientOptions> clientOptions)
: this(clientOptions.Value.Url, httpClient) // Call generated ctor
{
}
}
将 VS 连接服务 (NSwag) 注入 classes/controllers 的首选方法是什么。我在网上找到了很多使用这种形式的建议:
services.AddHttpClient<IClient, Client>((provider, client) =>
{
client.BaseAddress = new System.Uri("https://some.baseurl/");
});
但是这会导致错误
{"errorMessage":"Unable to resolve service for type 'System.String' while attempting to activate 'xxx.Client'."}
这来自 obj
中自动生成的客户端 class,它似乎在构造函数中强制使用字符串 BaseUrl,当然 DI 无法解析:
public Client(string baseUrl, System.Net.Http.HttpClient httpClient)
{
BaseUrl = baseUrl;
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
这个基础 URL 后来被强制进入 url 构建器代码,所以它不能真正被绕过。然而,即使是网络上对客户端 classes 使用部分扩展的解决方案似乎也完全忽略了 auto-gen class(如 here)中的 baseUrl。就好像它不存在一样(这很奇怪,NSwag 在生成不同的构造函数之前有吗?)。 class 是通过 csproj 生成的:
<ItemGroup>
<OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="xxx" ClassName="Client">
<SourceUri>https://localhost:44353/swagger/v1/swagger.json</SourceUri>
</OpenApiReference>
</ItemGroup>
这导致目标构建调用:
2>GenerateNSwagCSharp:
2> "C:\.<path>./tools/Win/NSwag.exe" openapi2csclient /className:Client /namespace:xxx /input:"C:\<projpath>\OpenAPIs\swagger.json" /output:"obj\swaggerClient.cs"
2>NSwag command line tool for .NET 4.6.1+ WinX64, toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))
那么,这是怎么做到的?潜在地,在不为代理 class 创建另一个代理 class 的情况下,我宁愿 DI 处理我的对象生命周期。如果可能的话,我也想避免使用 NSwagStudio,并希望保留 VS 提供的工具。
好吧,其实我是通过OpenApiReference
摸索着解决了这个问题,但是需要手动修改csproj文件。必须将额外的 Options
节点添加到 OpenApiReference
项目组,以指示 NSwag 不公开 BaseUrl 并生成一个接口,这可以简化设置 DI 的工作,而无需额外的代码。
Visual Studio 团队真的应该将这两个复选框添加到 OpenAPI 的连接服务 screens/configuration。
<ItemGroup>
<OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="xxx" ClassName="Client">
<SourceUri>https://localhost:44353/swagger/v1/swagger.json</SourceUri>
<Options>/UseBaseUrl:false /GenerateClientInterfaces:true</Options>
</OpenApiReference>
</ItemGroup>
现在只有一个 HttpClient
构造函数,NSwag 客户端代理使用它的基地址,因此 AddHttpClient
通过 DI 可以正常工作。
由于生成的 class 被标记为部分,您可以提供一个额外的构造函数并用 [ActivatorUtilitiesConstructor]
属性标记它。应用该属性可确保构造函数与依赖注入一起使用。您还可以在部分扩展中实现该接口。这是一个例子;
public partial class MyApiClient : IMyApiClient
{
[ActivatorUtilitiesConstructor] // This ctor will be used by DI
public MyApiClient(HttpClient httpClient, IOptions<MyApiClientOptions> clientOptions)
: this(clientOptions.Value.Url, httpClient) // Call generated ctor
{
}
}