What is the preferred way to make a VS connected service (NSwag) injected into classes/controllers. I have found a lot of suggestions on the net to use this form:
services.AddHttpClient<IClient, Client>((provider, client) =>
{
client.BaseAddress = new System.Uri("https://some.baseurl/");
});
However this results in the error
{"errorMessage":"Unable to resolve service for type 'System.String' while attempting to activate 'xxx.Client'."}
This comes from the auto-generated client class in obj
, which seems to force a string BaseUrl in constructor, which of course the DI cannot resolve:
public Client(string baseUrl, System.Net.Http.HttpClient httpClient)
{
BaseUrl = baseUrl;
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
This base URL is later forced into the url builder code, so it cannot really be bypassed. However, even the solutions on the net which use partial extensions to client classes seem to completely ignore baseUrl in auto-gen class (like here). As if it does not exist (which is weird, has the NSwag before generated different constructors?). The class is being generated via csproj:
<ItemGroup>
<OpenApiReference Include="OpenAPIs\swagger.json" CodeGenerator="NSwagCSharp" Namespace="xxx" ClassName="Client">
<SourceUri>https://localhost:44353/swagger/v1/swagger.json</SourceUri>
</OpenApiReference>
</ItemGroup>
And this results in the targeted build call:
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))
So, how is this being done? potentially, without creating another proxy class for a proxy class, I would rather that DI handles my object lifetimes. I would also like to avoid NSwagStudio if possible and would like to keep the tooling supplied by VS.
Open API and NSwag provide limited supports for enum , however, Swashbuckle supports even less. NSwag does support namespace and enum, however, not worrking well with the Swagger definition file generated by Swashbuckle.
NSwag is open source project for generating Swagger documents and integrating Swagger UI or ReDoc into ASP.NET Core web APIs. Additionally, NSwag offers approaches to generate C# and TypeScript client code for your API.
NSwag is a Swagger/OpenAPI 2.0 and 3.0 toolchain for . NET, . NET Core, Web API, ASP.NET Core, TypeScript (jQuery, AngularJS, Angular 2+, Aurelia, KnockoutJS and more) and other platforms, written in C#. The OpenAPI/Swagger specification uses JSON and JSON Schema to describe a RESTful web API.
Ok, I actually solved this problem by poking around through OpenApiReference
, but it requires manual modification of csproj file. Additional Options
node has to be added to OpenApiReference
item group, to instruct NSwag to NOT expose BaseUrl and to also generate an interface, which eases the work with setting up DI without additional code.
Visual Studio team should really add these two checkboxes to Connected Services screens/configuration for OpenAPI.
<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>
Now there is only an HttpClient
constructor, andNSwag client proxy uses base address from it, so AddHttpClient
works properly through DI.
As the generated class is marked partial you can provide an additional constructor and mark it with the [ActivatorUtilitiesConstructor]
attribute. Applying the attribute makes sure that constructor is used with Dependency Injection. You can also implement the interface in your partial extension. Here's an example of this;
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
{
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With