@team-supercharge/oasg 18.1.0 → 18.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/package.json +1 -1
- package/targets/dotnet-webapi-system-text-json/README.md +7 -0
- package/targets/dotnet-webapi-system-text-json/generate.sh +1 -1
- package/targets/dotnet-webapi-system-text-json/generator-config.json +1 -1
- package/targets/dotnet-webapi-system-text-json/templates/Project.csproj.mustache +5 -4
- package/targets/dotnet-webapi-system-text-json/templates/enumClass.mustache +24 -0
- package/targets/dotnet-webapi-system-text-json/templates/minimalEndpoints.mustache +3 -1
- package/targets/dotnet-webapi-system-text-json/templates/model.mustache +0 -1
- package/targets/dotnet-webapi-system-text-json/templates/serviceCollectionExtensions.mustache +70 -46
- package/targets/dotnet-webapi-system-text-json/templates/typeConverter.mustache +11 -0
package/README.md
CHANGED
|
@@ -1277,19 +1277,31 @@ with **System.Text.Json**, targeting **.NET 10** and marked
|
|
|
1277
1277
|
> implements each `I{ApiName}` and calls `app.MapAll{PackageName}Endpoints()`; the
|
|
1278
1278
|
> abstract MVC controllers are suppressed.
|
|
1279
1279
|
>
|
|
1280
|
-
> The
|
|
1281
|
-
>
|
|
1282
|
-
>
|
|
1283
|
-
>
|
|
1284
|
-
>
|
|
1280
|
+
> The package builds AOT-clean: it sets `<EnableRequestDelegateGenerator>` so the
|
|
1281
|
+
> minimal-API `Map*()` calls are statically generated (no reflection-based request
|
|
1282
|
+
> delegates), each model emits a source-generated `JsonSerializerContext` used by
|
|
1283
|
+
> its `ToJson()` (no Newtonsoft, no reflection `JsonSerializer`), and there is no
|
|
1284
|
+
> reflection-based enum converter. Building the package under the AOT analyzer
|
|
1285
|
+
> (`<IsAotCompatible>`) raises no IL2026/IL3050 warnings.
|
|
1285
1286
|
>
|
|
1286
|
-
>
|
|
1287
|
-
>
|
|
1288
|
-
>
|
|
1287
|
+
> Enums (de)serialize by their wire value (e.g. `"available"`): each generated enum
|
|
1288
|
+
> carries `[JsonConverter(typeof(JsonStringEnumConverter<T>))]` with per-member
|
|
1289
|
+
> `[JsonStringEnumMemberName]`, which the source generator honours.
|
|
1290
|
+
>
|
|
1291
|
+
> **JSON registration.** The package generates a `{PackageName}JsonSerializerContext`
|
|
1292
|
+
> (a source-generated `JsonSerializerContext` over every model, request/response type
|
|
1293
|
+
> and inline enum) plus an `Add{PackageName}JsonOptions()` extension that registers it
|
|
1294
|
+
> on the minimal-API JSON resolver chain. Call it in Program.cs:
|
|
1289
1295
|
>
|
|
1290
1296
|
> ```csharp
|
|
1291
1297
|
> builder.Services.Add{PackageName}JsonOptions();
|
|
1292
1298
|
> ```
|
|
1299
|
+
>
|
|
1300
|
+
> This is **required when the host is published with Native AOT** (`PublishAot=true`),
|
|
1301
|
+
> because AOT removes the reflection JSON fallback and the endpoints must resolve every
|
|
1302
|
+
> DTO through source generation. For a reflection-based (JIT) host it is harmless (the
|
|
1303
|
+
> reflection resolver already covers everything), but registering it still gives faster,
|
|
1304
|
+
> allocation-free metadata, so calling it unconditionally is recommended.
|
|
1293
1305
|
|
|
1294
1306
|
##### `withWolverineImplementation`
|
|
1295
1307
|
|
package/package.json
CHANGED
|
@@ -31,6 +31,12 @@ Everything lives in a single `EndpointRouteBuilderExtensions.cs`:
|
|
|
31
31
|
- **`ConfigureWolverineMessaging(this WolverineOptions, string localQueueName = "MyApi")`** —
|
|
32
32
|
routes every request type in the assembly to a local queue.
|
|
33
33
|
|
|
34
|
+
A separate `ServiceCollectionExtensions.cs` holds a source-generated
|
|
35
|
+
`{PackageName}JsonSerializerContext` (covering every model, request/response type
|
|
36
|
+
and inline enum) and an **`Add{PackageName}JsonOptions(this IServiceCollection)`**
|
|
37
|
+
extension that registers it on the minimal-API JSON resolver chain — call it in
|
|
38
|
+
Program.cs (required when publishing the host with Native AOT; harmless otherwise).
|
|
39
|
+
|
|
34
40
|
The `WolverineFx` package reference is added automatically.
|
|
35
41
|
|
|
36
42
|
---
|
|
@@ -61,6 +67,7 @@ using MyApi; // generated Map{ApiName}Endpoints / MapAllMyApiEndpoints / Configu
|
|
|
61
67
|
|
|
62
68
|
var builder = WebApplication.CreateBuilder(args);
|
|
63
69
|
builder.Services.AddHttpContextAccessor(); // see "Authentication" below
|
|
70
|
+
builder.Services.AddMyApiJsonOptions(); // register the source-gen JSON context (required for Native AOT)
|
|
64
71
|
|
|
65
72
|
builder.Host.UseWolverine(opts =>
|
|
66
73
|
{
|
|
@@ -26,7 +26,7 @@ if [ -f "$endpointsFile" ]; then
|
|
|
26
26
|
sed -i.bak 's/\.MapHttp/.Map/g' "$destFile" && rm -f "$destFile.bak"
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
|
-
# Same for the JSON-
|
|
29
|
+
# Same for the JSON source-gen context / service-collection extension (SupportingFiles file).
|
|
30
30
|
serviceExtFile="out/$targetId/ServiceCollectionExtensions.cs"
|
|
31
31
|
if [ -f "$serviceExtFile" ]; then
|
|
32
32
|
mv "$serviceExtFile" "out/$targetId/src/$packageName/ServiceCollectionExtensions.cs"
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
<Authors>{{packageAuthors}}</Authors>
|
|
6
6
|
<TargetFramework>net10.0</TargetFramework>
|
|
7
7
|
<IsAotCompatible>true</IsAotCompatible>
|
|
8
|
+
<!-- Statically generate the minimal-API request delegates (interceptors) so the
|
|
9
|
+
Map*() calls are trim-/AOT-safe instead of reflection-based at runtime.
|
|
10
|
+
Without this the [RequiresDynamicCode]/[RequiresUnreferencedCode] Map*
|
|
11
|
+
overloads raise IL3050/IL2026 under the AOT analyzer enabled above. -->
|
|
12
|
+
<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
|
|
8
13
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
9
14
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
|
10
15
|
<Version>{{packageVersion}}</Version>
|
|
@@ -38,9 +43,6 @@
|
|
|
38
43
|
{{^useFrameworkReference}}
|
|
39
44
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
|
40
45
|
{{/useFrameworkReference}}
|
|
41
|
-
{{^useSeparateModelProject}}
|
|
42
|
-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" {{#usePackageVersions}}Version="{{aspnetCoreVersion}}.0" {{/usePackageVersions}}/>
|
|
43
|
-
{{/useSeparateModelProject}}
|
|
44
46
|
{{#useSwashbuckle}}
|
|
45
47
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" {{#usePackageVersions}}Version="1.10.8" {{/usePackageVersions}}/>
|
|
46
48
|
{{#useNewtonsoft}}
|
|
@@ -57,7 +59,6 @@
|
|
|
57
59
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" {{#usePackageVersions}}Version="{{newtonsoftVersion}}" {{/usePackageVersions}}/>
|
|
58
60
|
{{/useNewtonsoft}}
|
|
59
61
|
{{/useSwashbuckle}}
|
|
60
|
-
<PackageReference Include="JsonSubTypes" {{#usePackageVersions}}Version="1.8.0" {{/usePackageVersions}}/>
|
|
61
62
|
</ItemGroup>
|
|
62
63
|
<ItemGroup>
|
|
63
64
|
<!--<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="{{aspnetCoreVersion}}.0" />-->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
/// <summary>
|
|
3
|
+
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}}
|
|
4
|
+
/// </summary>
|
|
5
|
+
{{#description}}
|
|
6
|
+
/// <value>{{{.}}}</value>
|
|
7
|
+
{{/description}}
|
|
8
|
+
{{! AOT-safe enum (de)serialization: the source-generated, generic }}
|
|
9
|
+
{{! JsonStringEnumConverter<T> (no runtime MakeGenericType/Activator) plus the }}
|
|
10
|
+
{{! per-member JsonStringEnumMemberName below, which maps each value to its wire }}
|
|
11
|
+
{{! form. The attribute travels with the type, so enums round-trip as strings }}
|
|
12
|
+
{{! for reflection-based (JIT) hosts and source-generated (AOT) hosts alike. }}
|
|
13
|
+
[JsonConverter(typeof(JsonStringEnumConverter<{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))]
|
|
14
|
+
public enum {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
|
|
15
|
+
{
|
|
16
|
+
{{#allowableValues}}{{#enumVars}}
|
|
17
|
+
/// <summary>
|
|
18
|
+
/// Enum {{name}} for {{{value}}}
|
|
19
|
+
/// </summary>
|
|
20
|
+
{{#isString}}[EnumMember(Value = "{{{value}}}")]
|
|
21
|
+
[JsonStringEnumMemberName("{{{value}}}")]{{/isString}}
|
|
22
|
+
{{name}}{{^isString}} = {{{value}}}{{/isString}}{{#isString}} = {{-index}}{{/isString}}{{^-last}},
|
|
23
|
+
{{/-last}}{{/enumVars}}{{/allowableValues}}
|
|
24
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// <auto-generated/>
|
|
2
2
|
#nullable enable
|
|
3
|
+
using System;
|
|
3
4
|
using System.Collections.Generic;
|
|
4
5
|
using System.Threading;
|
|
5
6
|
using System.Threading.Tasks;
|
|
@@ -25,6 +26,7 @@ namespace {{packageName}}
|
|
|
25
26
|
{
|
|
26
27
|
{{#operations}}
|
|
27
28
|
{{#operation}}
|
|
29
|
+
/// <summary>Application logic for the {{operationId}} operation.</summary>
|
|
28
30
|
Task<IResult> {{operationId}}({{#allParams}}{{>paramType}} {{paramName}}, {{/allParams}}CancellationToken cancellationToken);
|
|
29
31
|
{{/operation}}
|
|
30
32
|
{{/operations}}
|
|
@@ -76,7 +78,7 @@ namespace {{packageName}}
|
|
|
76
78
|
{{/apis}}
|
|
77
79
|
{{/apiInfo}}
|
|
78
80
|
/// <summary>Maps the endpoints of every generated API.</summary>
|
|
79
|
-
public static IEndpointRouteBuilder MapAll{{packageName}}Endpoints(this IEndpointRouteBuilder endpoints)
|
|
81
|
+
public static IEndpointRouteBuilder MapAll{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}Endpoints(this IEndpointRouteBuilder endpoints)
|
|
80
82
|
{
|
|
81
83
|
{{#apiInfo}}
|
|
82
84
|
{{#apis}}
|
package/targets/dotnet-webapi-system-text-json/templates/serviceCollectionExtensions.mustache
CHANGED
|
@@ -1,66 +1,90 @@
|
|
|
1
1
|
// <auto-generated/>
|
|
2
|
+
#nullable enable
|
|
2
3
|
using System;
|
|
3
4
|
using System.Collections.Generic;
|
|
4
|
-
using System.Reflection;
|
|
5
|
-
using System.Runtime.Serialization;
|
|
6
|
-
using System.Text.Json;
|
|
7
5
|
using System.Text.Json.Serialization;
|
|
8
|
-
using Microsoft.AspNetCore.Http.Json;
|
|
9
6
|
using Microsoft.Extensions.DependencyInjection;
|
|
7
|
+
using {{packageName}}.Models;
|
|
10
8
|
|
|
11
9
|
namespace {{packageName}}
|
|
12
10
|
{
|
|
13
11
|
/// <summary>
|
|
14
|
-
///
|
|
15
|
-
///
|
|
12
|
+
/// System.Text.Json source-generation context covering every type this package
|
|
13
|
+
/// (de)serializes through its minimal-API endpoints: each generated model, each
|
|
14
|
+
/// operation request/response type, and each inline enum. Registering it (see
|
|
15
|
+
/// <see cref="{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}ServiceCollectionExtensions" />)
|
|
16
|
+
/// lets the endpoints resolve JSON metadata via source generation, which is what
|
|
17
|
+
/// makes the package safe to trim and to publish with Native AOT — the pipeline
|
|
18
|
+
/// never falls back to reflection.
|
|
16
19
|
/// </summary>
|
|
17
|
-
|
|
20
|
+
/// <remarks>
|
|
21
|
+
/// Duplicate <c>[JsonSerializable]</c> entries are coalesced by the generator, so
|
|
22
|
+
/// request/response types that are also models are harmless. Inline enums are
|
|
23
|
+
/// registered with an explicit <c>TypeInfoPropertyName</c> because two models can
|
|
24
|
+
/// declare equally-named nested enums (e.g. <c>Order.StatusEnum</c> and
|
|
25
|
+
/// <c>Pet.StatusEnum</c>), which would otherwise collide under SYSLIB1031.
|
|
26
|
+
/// </remarks>
|
|
27
|
+
[JsonSerializable(typeof(string))]
|
|
28
|
+
{{#models}}
|
|
29
|
+
{{#model}}
|
|
30
|
+
[JsonSerializable(typeof({{classname}}))]
|
|
31
|
+
{{#vars}}
|
|
32
|
+
{{#isEnum}}
|
|
33
|
+
{{^complexType}}
|
|
34
|
+
[JsonSerializable(typeof({{classname}}.{{datatypeWithEnum}}), TypeInfoPropertyName = "{{classname}}{{datatypeWithEnum}}")]
|
|
35
|
+
{{/complexType}}
|
|
36
|
+
{{/isEnum}}
|
|
37
|
+
{{#items.isEnum}}
|
|
38
|
+
{{#items}}
|
|
39
|
+
{{^complexType}}
|
|
40
|
+
[JsonSerializable(typeof({{classname}}.{{datatypeWithEnum}}), TypeInfoPropertyName = "{{classname}}{{datatypeWithEnum}}")]
|
|
41
|
+
{{/complexType}}
|
|
42
|
+
{{/items}}
|
|
43
|
+
{{/items.isEnum}}
|
|
44
|
+
{{/vars}}
|
|
45
|
+
{{/model}}
|
|
46
|
+
{{/models}}
|
|
47
|
+
{{#apiInfo}}
|
|
48
|
+
{{#apis}}
|
|
49
|
+
{{#operations}}
|
|
50
|
+
{{#operation}}
|
|
51
|
+
{{^isResponseFile}}
|
|
52
|
+
{{#returnType}}
|
|
53
|
+
[JsonSerializable(typeof({{{returnType}}}))]
|
|
54
|
+
{{/returnType}}
|
|
55
|
+
{{/isResponseFile}}
|
|
56
|
+
{{#bodyParam}}
|
|
57
|
+
{{^isBinary}}
|
|
58
|
+
[JsonSerializable(typeof({{#isArray}}List<{{{items.dataType}}}>{{/isArray}}{{^isArray}}{{{baseType}}}{{/isArray}}))]
|
|
59
|
+
{{/isBinary}}
|
|
60
|
+
{{/bodyParam}}
|
|
61
|
+
{{/operation}}
|
|
62
|
+
{{/operations}}
|
|
63
|
+
{{/apis}}
|
|
64
|
+
{{/apiInfo}}
|
|
65
|
+
public partial class {{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}JsonSerializerContext : JsonSerializerContext
|
|
18
66
|
{
|
|
19
|
-
public static IServiceCollection Add{{packageName}}JsonOptions(this IServiceCollection services)
|
|
20
|
-
{
|
|
21
|
-
services.Configure<JsonOptions>(opts =>
|
|
22
|
-
opts.SerializerOptions.Converters.Add(new EnumMemberJsonConverterFactory()));
|
|
23
|
-
return services;
|
|
24
|
-
}
|
|
25
67
|
}
|
|
26
68
|
|
|
27
69
|
/// <summary>
|
|
28
|
-
///
|
|
29
|
-
///
|
|
70
|
+
/// Registers the System.Text.Json source-generation context for this generated
|
|
71
|
+
/// package. Call <c>builder.Services.Add{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}JsonOptions()</c>
|
|
72
|
+
/// in Program.cs so the minimal-API endpoints resolve JSON metadata via source
|
|
73
|
+
/// generation — required when the host is published with Native AOT, harmless
|
|
74
|
+
/// (a no-op the reflection resolver already covers) for a JIT host.
|
|
30
75
|
/// </summary>
|
|
31
|
-
|
|
76
|
+
public static class {{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}ServiceCollectionExtensions
|
|
32
77
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
internal sealed class EnumMemberJsonConverter<T> : JsonConverter<T> where T : struct, Enum
|
|
41
|
-
{
|
|
42
|
-
private static readonly Dictionary<string, T> _read;
|
|
43
|
-
private static readonly Dictionary<T, string> _write;
|
|
44
|
-
|
|
45
|
-
static EnumMemberJsonConverter()
|
|
78
|
+
/// <summary>
|
|
79
|
+
/// Inserts <see cref="{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}JsonSerializerContext" />
|
|
80
|
+
/// at the front of the minimal-API JSON type-info resolver chain.
|
|
81
|
+
/// </summary>
|
|
82
|
+
public static IServiceCollection Add{{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}JsonOptions(this IServiceCollection services)
|
|
46
83
|
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
var wireValue = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? field.Name;
|
|
52
|
-
var enumValue = (T)field.GetValue(null)!;
|
|
53
|
-
_read[wireValue] = enumValue;
|
|
54
|
-
_write[enumValue] = wireValue;
|
|
55
|
-
}
|
|
84
|
+
services.ConfigureHttpJsonOptions(options =>
|
|
85
|
+
options.SerializerOptions.TypeInfoResolverChain.Insert(
|
|
86
|
+
0, {{#lambda.pascalcase}}{{packageName}}{{/lambda.pascalcase}}JsonSerializerContext.Default));
|
|
87
|
+
return services;
|
|
56
88
|
}
|
|
57
|
-
|
|
58
|
-
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
|
|
59
|
-
_read.TryGetValue(reader.GetString() ?? string.Empty, out var value)
|
|
60
|
-
? value
|
|
61
|
-
: throw new JsonException($"Unknown value '{reader.GetString()}' for {typeof(T).Name}");
|
|
62
|
-
|
|
63
|
-
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) =>
|
|
64
|
-
writer.WriteStringValue(_write.TryGetValue(value, out var s) ? s : value.ToString());
|
|
65
89
|
}
|
|
66
90
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// <auto-generated/>
|
|
2
|
+
//
|
|
3
|
+
// Intentionally empty for the System.Text.Json / Native-AOT target.
|
|
4
|
+
//
|
|
5
|
+
// The default aspnetcore template emits an MVC `CustomEnumConverter<T>`
|
|
6
|
+
// (a TypeConverter whose body calls the reflection-based
|
|
7
|
+
// JsonSerializer.Deserialize<T>, which raises IL2026/IL3050 under the AOT
|
|
8
|
+
// analyzer). This target suppresses MVC controllers and (de)serializes enums
|
|
9
|
+
// via [JsonConverter(typeof(JsonStringEnumConverter<T>))] + [JsonStringEnumMemberName]
|
|
10
|
+
// on each generated enum, so the converter is unused — and is omitted here to
|
|
11
|
+
// keep the package AOT-clean.
|