Skip to content

Commit 54c31ed

Browse files
committed
Update to NSwag 14, distribute our recommended NSwag MSBuild properties from NuGet package
1 parent 9c74c33 commit 54c31ed

File tree

14 files changed

+175
-136
lines changed

14 files changed

+175
-136
lines changed

docs/usage/openapi-client.md

+28-37
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ After [enabling OpenAPI](~/usage/openapi.md), you can generate a typed JSON:API
77
> [client libraries](https://jsonapi.org/implementations/#client-libraries).
88
99
The following code generators are supported, though you may try others as well:
10-
- [NSwag](https://github.com/RicoSuter/NSwag): Produces clients for C# and TypeScript
10+
- [NSwag](https://github.com/RicoSuter/NSwag) (v14.1 or higher): Produces clients for C# and TypeScript
1111
- [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/overview): Produces clients for C#, Go, Java, PHP, Python, Ruby, Swift and TypeScript
1212

1313
# [NSwag](#tab/nswag)
@@ -39,11 +39,11 @@ To generate your C# client, follow the steps below.
3939
### Visual Studio
4040

4141
The easiest way to get started is by using the built-in capabilities of Visual Studio.
42-
The following steps describe how to generate and use a JSON:API client in C#, using our package.
42+
The following steps describe how to generate and use a JSON:API client in C#, combined with our NuGet package.
4343

4444
1. In **Solution Explorer**, right-click your client project, select **Add** > **Service Reference** and choose **OpenAPI**.
4545

46-
2. On the next page, specify the OpenAPI URL to your JSON:API server, for example: `http://localhost:14140/swagger/v1/swagger.json`.
46+
1. On the next page, specify the OpenAPI URL to your JSON:API server, for example: `http://localhost:14140/swagger/v1/swagger.json`.
4747
Specify `ExampleApiClient` as the class name, optionally provide a namespace and click **Finish**.
4848
Visual Studio now downloads your swagger.json and updates your project file.
4949
This adds a pre-build step that generates the client code.
@@ -52,25 +52,15 @@ The following steps describe how to generate and use a JSON:API client in C#, us
5252
> To later re-download swagger.json and regenerate the client code,
5353
> right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon.
5454
55-
3. Although not strictly required, we recommend running package update now, which fixes some issues.
55+
1. Run package update now, which fixes incompatibilities and bugs from older versions.
5656

57-
> [!WARNING]
58-
> NSwag v14 is currently *incompatible* with JsonApiDotNetCore (tracked [here](https://github.com/RicoSuter/NSwag/issues/4662)).
59-
> Stick with v13.x for the moment.
60-
61-
4. Add our client package to your project:
57+
1. Add our client package to your project:
6258

6359
```
6460
dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag
6561
```
6662
67-
5. Add the following line inside the **OpenApiReference** section in your project file:
68-
69-
```xml
70-
<Options>/GenerateExceptionClasses:false /GenerateNullableReferenceTypes:true /GenerateOptionalPropertiesAsNullable:true /GenerateOptionalParameters:true /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag</Options>
71-
```
72-
73-
6. Add the following glue code to connect our package with your generated code.
63+
1. Add the following glue code to connect our package with your generated code.
7464
7565
> [!NOTE]
7666
> The class name must be the same as specified in step 2.
@@ -83,14 +73,15 @@ The following steps describe how to generate and use a JSON:API client in C#, us
8373
8474
partial class ExampleApiClient : JsonApiClient
8575
{
86-
partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
76+
partial void Initialize()
8777
{
88-
SetSerializerSettingsForJsonApi(settings);
78+
_instanceSettings = new JsonSerializerSettings(_settings.Value);
79+
SetSerializerSettingsForJsonApi(_instanceSettings);
8980
}
9081
}
9182
```
9283
93-
7. Add code that calls one of your JSON:API endpoints.
84+
1. Add code that calls one of your JSON:API endpoints.
9485
9586
```c#
9687
using var httpClient = new HttpClient();
@@ -101,15 +92,15 @@ The following steps describe how to generate and use a JSON:API client in C#, us
10192
["filter"] = "has(assignedTodoItems)",
10293
["sort"] = "-lastName",
10394
["page[size]"] = "5"
104-
}, null);
95+
});
10596
10697
foreach (var person in getResponse.Data)
10798
{
10899
Console.WriteLine($"Found person {person.Id}: {person.Attributes!.DisplayName}");
109100
}
110101
```
111102
112-
8. Extend your demo code to send a partial PATCH request with the help of our package:
103+
1. Extend the demo code to send a partial PATCH request with the help of our package:
113104
114105
```c#
115106
var updatePersonRequest = new UpdatePersonRequestDocument
@@ -163,16 +154,15 @@ Alternatively, the following section shows what to add to your client project fi
163154
<ItemGroup>
164155
<PackageReference Include="Microsoft.Extensions.ApiDescription.Client" Version="8.0.*" PrivateAssets="all" />
165156
<PackageReference Include="Newtonsoft.Json" Version="13.0.*" />
166-
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.20.*" PrivateAssets="all" />
157+
<PackageReference Include="NSwag.ApiDescription.Client" Version="14.1.*" PrivateAssets="all" />
167158
</ItemGroup>
168159
169160
<ItemGroup>
170161
<OpenApiReference Include="OpenAPIs\swagger.json">
171162
<SourceUri>http://localhost:14140/swagger/v1/swagger.json</SourceUri>
172-
<CodeGenerator>NSwagCSharp</CodeGenerator>
173-
<ClassName>ExampleApiClient</ClassName>
174-
<OutputPath>ExampleApiClient.cs</OutputPath>
175-
<Options>/GenerateExceptionClasses:false /GenerateNullableReferenceTypes:true /GenerateOptionalPropertiesAsNullable:true /GenerateOptionalParameters:true /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag</Options>
163+
<CodeGenerator>NSwagCSharp</CodeGenerator>
164+
<ClassName>ExampleApiClient</ClassName>
165+
<OutputPath>ExampleApiClient.cs</OutputPath>
176166
</OpenApiReference>
177167
</ItemGroup>
178168
```
@@ -203,23 +193,24 @@ Various switches enable you to tweak the client generation to your needs. See th
203193

204194
# [NSwag](#tab/nswag)
205195

206-
The `OpenApiReference` element in the project file accepts an `Options` element to pass additional settings to the client generator,
207-
which are listed [here](https://github.com/RicoSuter/NSwag/blob/master/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs).
208-
A guide with common best practices is available [here](https://stevetalkscode.co.uk/openapireference-commands).
196+
The `OpenApiReference` can be customized using various [NSwag-specific MSBuild properties](https://github.com/RicoSuter/NSwag/blob/7d6df3af95081f3f0ed6dee04be8d27faa86f91a/src/NSwag.ApiDescription.Client/NSwag.ApiDescription.Client.props).
197+
See [the source code](https://github.com/RicoSuter/NSwag/blob/master/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs) for their meaning.
209198

210-
For example, the following section puts the generated code in a namespace and generates an interface (which is handy for dependency injection):
199+
> [!NOTE]
200+
> Earlier versions of NSwag required the use of `<Options>` to specify command-line switches directly.
201+
> This is no longer recommended and may conflict with the new MSBuild properties.
202+
203+
For example, the following section puts the generated code in a namespace and generates an interface (handy when writing tests):
211204

212205
```xml
213206
<OpenApiReference Include="swagger.json">
214207
<Namespace>ExampleProject.GeneratedCode</Namespace>
215208
<ClassName>SalesApiClient</ClassName>
216209
<CodeGenerator>NSwagCSharp</CodeGenerator>
217-
<Options>/GenerateClientInterfaces:true</Options>
210+
<NSwagGenerateClientInterfaces>true</NSwagGenerateClientInterfaces>
218211
</OpenApiReference>
219212
```
220213

221-
Likewise, you can enable nullable reference types by adding `/GenerateNullableReferenceTypes:true /GenerateOptionalPropertiesAsNullable:true /GenerateOptionalParameters:true`.
222-
223214
# [Kiota](#tab/kiota)
224215

225216
The available command-line switches for Kiota are described [here](https://learn.microsoft.com/en-us/openapi/kiota/using#client-generation).
@@ -257,10 +248,10 @@ The use of HTTP headers varies per client generator. To use [ETags for caching](
257248

258249
# [NSwag](#tab/nswag)
259250

260-
NSwag needs extra settings to make response headers accessible. Specify the following in the `<Options>` element of your project file:
251+
To gain access to HTTP response headers, add the following in a `PropertyGroup` or directly in the `OpenApiReference`:
261252

262253
```
263-
/GenerateExceptionClasses:false /WrapResponses:true /GenerateResponseClasses:false /ResponseClass:ApiResponse /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag
254+
<NSwagWrapResponses>true</NSwagWrapResponses>
264255
```
265256

266257
This enables the following code, which is explained below:
@@ -272,7 +263,7 @@ Console.WriteLine($"Retrieved {getResponse.Result?.Data.Count ?? 0} people.");
272263

273264
// wait some time...
274265
275-
getResponse = await ApiResponse.TranslateAsync(() => apiClient.GetPersonCollectionAsync(null, eTag));
266+
getResponse = await ApiResponse.TranslateAsync(() => apiClient.GetPersonCollectionAsync(if_None_Match: eTag));
276267

277268
if (getResponse is { StatusCode: (int)HttpStatusCode.NotModified, Result: null })
278269
{
@@ -287,7 +278,7 @@ If you only want to ask whether data has changed without fetching it, use a HEAD
287278

288279
# [Kiota](#tab/kiota)
289280

290-
Use `HeadersInspectionHandlerOption` to gain access to response headers. For example:
281+
Use `HeadersInspectionHandlerOption` to gain access to HTTP response headers. For example:
291282

292283
```c#
293284
var headerInspector = new HeadersInspectionHandlerOption

package-versions.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<InheritDocVersion>2.0.*</InheritDocVersion>
2121
<KiotaVersion>1.*</KiotaVersion>
2222
<MicrosoftApiClientVersion>8.0.*</MicrosoftApiClientVersion>
23-
<NSwagApiClientVersion>13.20.*</NSwagApiClientVersion>
23+
<NSwagApiClientVersion>14.1.*</NSwagApiClientVersion>
2424
<NewtonsoftJsonVersion>13.0.*</NewtonsoftJsonVersion>
2525
<SourceLinkVersion>8.0.*</SourceLinkVersion>
2626
<SwashbuckleVersion>6.*-*</SwashbuckleVersion>

src/Examples/OpenApiNSwagClientExample/ExampleApiClient.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ namespace OpenApiNSwagClientExample;
77
[UsedImplicitly(ImplicitUseTargetFlags.Itself)]
88
public partial class ExampleApiClient : JsonApiClient
99
{
10-
partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
10+
partial void Initialize()
1111
{
12-
SetSerializerSettingsForJsonApi(settings);
12+
_instanceSettings = new JsonSerializerSettings(_settings.Value);
1313

1414
#if DEBUG
15-
settings.Formatting = Formatting.Indented;
15+
_instanceSettings.Formatting = Formatting.Indented;
1616
#endif
17+
18+
SetSerializerSettingsForJsonApi(_instanceSettings);
1719
}
1820
}

src/Examples/OpenApiNSwagClientExample/OpenApiNSwagClientExample.csproj

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<Import Project="..\..\..\package-versions.props" />
8+
<Import Project="..\..\JsonApiDotNetCore.OpenApi.Client.NSwag\Build\JsonApiDotNetCore.OpenApi.Client.NSwag.props" />
89

910
<ItemGroup>
1011
<ProjectReference Include="..\..\JsonApiDotNetCore.OpenApi.Client.NSwag\JsonApiDotNetCore.OpenApi.Client.NSwag.csproj" />
@@ -20,11 +21,11 @@
2021

2122
<ItemGroup>
2223
<OpenApiReference Include="..\JsonApiDotNetCoreExample\GeneratedSwagger\JsonApiDotNetCoreExample.json">
23-
<Namespace>OpenApiNSwagClientExample</Namespace>
24-
<ClassName>ExampleApiClient</ClassName>
25-
<OutputPath>ExampleApiClient.cs</OutputPath>
26-
<CodeGenerator>NSwagCSharp</CodeGenerator>
27-
<Options>/GenerateExceptionClasses:false /WrapResponses:true /GenerateResponseClasses:false /ResponseClass:ApiResponse /GenerateNullableReferenceTypes:true /GenerateOptionalPropertiesAsNullable:true /GenerateOptionalParameters:true /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag</Options>
24+
<Name>ExampleApi</Name>
25+
<Namespace>$(MSBuildProjectName)</Namespace>
26+
<ClassName>%(Name)Client</ClassName>
27+
<OutputPath>%(ClassName).cs</OutputPath>
28+
<NSwagWrapResponses>true</NSwagWrapResponses>
2829
</OpenApiReference>
2930
</ItemGroup>
3031
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project>
2+
<PropertyGroup>
3+
<CodeGenerator>NSwagCSharp</CodeGenerator>
4+
<NSwagGenerateExceptionClasses>false</NSwagGenerateExceptionClasses>
5+
<NSwagGenerateResponseClasses>false</NSwagGenerateResponseClasses>
6+
<NSwagResponseClass>ApiResponse</NSwagResponseClass>
7+
<NSwagAdditionalNamespaceUsages>JsonApiDotNetCore.OpenApi.Client.NSwag</NSwagAdditionalNamespaceUsages>
8+
<NSwagGenerateNullableReferenceTypes>true</NSwagGenerateNullableReferenceTypes>
9+
<NSwagGenerateOptionalPropertiesAsNullable>true</NSwagGenerateOptionalPropertiesAsNullable>
10+
<NSwagGenerateOptionalParameters>true</NSwagGenerateOptionalParameters>
11+
</PropertyGroup>
12+
</Project>

src/JsonApiDotNetCore.OpenApi.Client.NSwag/JsonApiDotNetCore.OpenApi.Client.NSwag.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<ItemGroup>
2828
<None Include="..\..\package-icon.png" Visible="false" Pack="True" PackagePath="" />
2929
<None Include="..\..\PackageReadme.md" Visible="false" Pack="True" PackagePath="" />
30+
<None Include="Build\*.props" Pack="True" PackagePath="build" />
3031
</ItemGroup>
3132

3233
<ItemGroup>

src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ private static void AddQueryStringParameters(OpenApiOperation operation, bool is
540540
// The next best thing is to expose the query string parameters as unstructured and optional.
541541
// - This makes SwaggerUI ask for JSON, which is a bit odd, but it works. For example: {"sort":"-id"} produces: ?sort=-id
542542
// - This makes NSwag produce a C# client with method signature: GetAsync(IDictionary<string, string?>? query)
543-
// when combined with <Options>/GenerateNullableReferenceTypes:true</Options> in the project file.
543+
// when combined with <NSwagGenerateNullableReferenceTypes>true</NSwagGenerateNullableReferenceTypes> in the project file.
544544

545545
operation.Parameters.Add(new OpenApiParameter
546546
{

test/OpenApiNSwagClientTests/LegacyOpenApi/GeneratedCode/LegacyClient.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ namespace OpenApiNSwagClientTests.LegacyOpenApi.GeneratedCode;
55

66
internal partial class LegacyClient : JsonApiClient
77
{
8-
partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings)
8+
partial void Initialize()
99
{
10-
SetSerializerSettingsForJsonApi(settings);
10+
_instanceSettings = new JsonSerializerSettings(_settings.Value)
11+
{
12+
Formatting = Formatting.Indented
13+
};
1114

12-
settings.Formatting = Formatting.Indented;
15+
SetSerializerSettingsForJsonApi(_instanceSettings);
1316
}
1417
}

0 commit comments

Comments
 (0)