From 718452b29d8dd50a62fc6896bafa9a368556243f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 4 Jun 2024 16:17:23 +0800 Subject: [PATCH 1/6] gRPC JSON transcoding + Microsoft.AspNetCore.OpenApi --- AspNetCore.sln | 19 ++++++++ src/Grpc/Grpc.slnf | 16 +++++++ .../GrpcJsonTranscodingDescriptionProvider.cs | 48 +++++++++++++------ .../test/testassets/Sandbox/Sandbox.csproj | 1 + .../test/testassets/Sandbox/Startup.cs | 3 ++ 5 files changed, 72 insertions(+), 15 deletions(-) diff --git a/AspNetCore.sln b/AspNetCore.sln index 7eba541ea0a0..898e31e3bc75 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1818,6 +1818,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentSample", "src\To EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorUnitedApp.Client", "src\Components\Samples\BlazorUnitedApp.Client\BlazorUnitedApp.Client.csproj", "{757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Grpc.OpenApi", "src\Grpc\JsonTranscoding\src\Microsoft.AspNetCore.Grpc.OpenApi\Microsoft.AspNetCore.Grpc.OpenApi.csproj", "{DE901A71-3EBD-4D25-B59F-F47C3F149361}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -10995,6 +10997,22 @@ Global {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x64.Build.0 = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.ActiveCfg = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.Build.0 = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|arm64.ActiveCfg = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|arm64.Build.0 = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x64.ActiveCfg = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x64.Build.0 = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x86.ActiveCfg = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x86.Build.0 = Debug|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|Any CPU.Build.0 = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|arm64.ActiveCfg = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|arm64.Build.0 = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x64.ActiveCfg = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x64.Build.0 = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x86.ActiveCfg = Release|Any CPU + {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -11893,6 +11911,7 @@ Global {6A19D94D-2BC6-4198-BE2E-342688FDBA4B} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {D8F7091E-A2D1-4E81-BA7C-97EAE392D683} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} + {DE901A71-3EBD-4D25-B59F-F47C3F149361} = {151E6F9E-107B-4DDC-A2B1-95115801FD14} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/src/Grpc/Grpc.slnf b/src/Grpc/Grpc.slnf index 98592f405ac7..ec8b230cb98a 100644 --- a/src/Grpc/Grpc.slnf +++ b/src/Grpc/Grpc.slnf @@ -8,6 +8,7 @@ "src\\Grpc\\Interop\\test\\testassets\\InteropWebsite\\InteropWebsite.csproj", "src\\Grpc\\JsonTranscoding\\perf\\Microsoft.AspNetCore.Grpc.Microbenchmarks\\Microsoft.AspNetCore.Grpc.Microbenchmarks.csproj", "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.JsonTranscoding\\Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj", + "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.OpenApi\\Microsoft.AspNetCore.Grpc.OpenApi.csproj", "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.Swagger\\Microsoft.AspNetCore.Grpc.Swagger.csproj", "src\\Grpc\\JsonTranscoding\\test\\Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests\\Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests.csproj", "src\\Grpc\\JsonTranscoding\\test\\Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests\\Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.csproj", @@ -18,13 +19,28 @@ "src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj", "src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj", "src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj", + "src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj", + "src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj", "src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj", "src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj", + "src\\Http\\Http.Extensions\\gen\\Microsoft.AspNetCore.Http.RequestDelegateGenerator.csproj", "src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj", "src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj", + "src\\Http\\Http.Results\\src\\Microsoft.AspNetCore.Http.Results.csproj", "src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj", + "src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj", + "src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj", + "src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj", "src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj", + "src\\Middleware\\OutputCaching\\src\\Microsoft.AspNetCore.OutputCaching.csproj", + "src\\Middleware\\ResponseCaching.Abstractions\\src\\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj", + "src\\Mvc\\Mvc.Abstractions\\src\\Microsoft.AspNetCore.Mvc.Abstractions.csproj", + "src\\Mvc\\Mvc.ApiExplorer\\src\\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj", + "src\\Mvc\\Mvc.Core\\src\\Microsoft.AspNetCore.Mvc.Core.csproj", "src\\ObjectPool\\src\\Microsoft.Extensions.ObjectPool.csproj", + "src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj", + "src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj", + "src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj", "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj", "src\\Testing\\src\\Microsoft.AspNetCore.InternalTesting.csproj" ] diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs index ee6fe6284b3d..d614ffaa97bb 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs @@ -106,40 +106,56 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, var field = routeParameter.Value.DescriptorsPath.Last(); var parameterName = ServiceDescriptorHelpers.FormatUnderscoreName(field.Name, pascalCase: true, preservePeriod: false); var propertyInfo = field.ContainingType.ClrType.GetProperty(parameterName); + var fieldType = MessageDescriptorHelpers.ResolveFieldType(field); // If from a property, create model as property to get its XML comments. var identity = propertyInfo != null - ? ModelMetadataIdentity.ForProperty(propertyInfo, MessageDescriptorHelpers.ResolveFieldType(field), field.ContainingType.ClrType) - : ModelMetadataIdentity.ForType(MessageDescriptorHelpers.ResolveFieldType(field)); + ? ModelMetadataIdentity.ForProperty(propertyInfo, fieldType, field.ContainingType.ClrType) + : ModelMetadataIdentity.ForType(fieldType); apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = routeParameter.Value.JsonPath, ModelMetadata = new GrpcModelMetadata(identity), Source = BindingSource.Path, - DefaultValue = string.Empty + DefaultValue = string.Empty, + Type = fieldType }); } var bodyDescriptor = ServiceDescriptorHelpers.ResolveBodyDescriptor(httpRule.Body, methodMetadata.ServiceType, methodDescriptor); if (bodyDescriptor != null) { - // If from a property, create model as property to get its XML comments. - var identity = bodyDescriptor.PropertyInfo != null - ? ModelMetadataIdentity.ForProperty(bodyDescriptor.PropertyInfo, bodyDescriptor.PropertyInfo.PropertyType, bodyDescriptor.PropertyInfo.DeclaringType!) - : ModelMetadataIdentity.ForType(bodyDescriptor.Descriptor.ClrType); + ModelMetadataIdentity identity; + Type type; + ControllerParameterDescriptor? parameterDescriptor = null; - // Or if from a parameter, create model as parameter to get its XML comments. - var parameterDescriptor = bodyDescriptor.ParameterInfo != null - ? new ControllerParameterDescriptor { ParameterInfo = bodyDescriptor.ParameterInfo } - : null; + if (bodyDescriptor.PropertyInfo != null) + { + // If from a property, create model as property to get its XML comments. + identity = ModelMetadataIdentity.ForProperty(bodyDescriptor.PropertyInfo, bodyDescriptor.PropertyInfo.PropertyType, bodyDescriptor.PropertyInfo.DeclaringType!); + type = bodyDescriptor.PropertyInfo.PropertyType; + } + else if (bodyDescriptor.ParameterInfo != null) + { + // Or if from a parameter, create model as parameter to get its XML comments. + identity = ModelMetadataIdentity.ForType(bodyDescriptor.Descriptor.ClrType); + type = bodyDescriptor.Descriptor.ClrType; + parameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = bodyDescriptor.ParameterInfo }; + } + else + { + identity = ModelMetadataIdentity.ForType(bodyDescriptor.Descriptor.ClrType); + type = bodyDescriptor.Descriptor.ClrType; + } apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = "Input", ModelMetadata = new GrpcModelMetadata(identity), Source = BindingSource.Body, - ParameterDescriptor = parameterDescriptor! + ParameterDescriptor = parameterDescriptor!, + Type = type }); } @@ -148,18 +164,20 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, { var field = queryDescription.Value; var propertyInfo = field.ContainingType.ClrType.GetProperty(field.PropertyName); + var fieldType = MessageDescriptorHelpers.ResolveFieldType(field); // If from a property, create model as property to get its XML comments. var identity = propertyInfo != null - ? ModelMetadataIdentity.ForProperty(propertyInfo, MessageDescriptorHelpers.ResolveFieldType(field), field.ContainingType.ClrType) - : ModelMetadataIdentity.ForType(MessageDescriptorHelpers.ResolveFieldType(field)); + ? ModelMetadataIdentity.ForProperty(propertyInfo, fieldType, field.ContainingType.ClrType) + : ModelMetadataIdentity.ForType(fieldType); apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = queryDescription.Key, ModelMetadata = new GrpcModelMetadata(identity), Source = BindingSource.Query, - DefaultValue = string.Empty + DefaultValue = string.Empty, + Type = fieldType }); } diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj index fa3e2095d4e1..146893e0dd85 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj @@ -10,5 +10,6 @@ + diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs index 4106b2bd9cc4..93d34f61b220 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs @@ -12,6 +12,8 @@ public void ConfigureServices(IServiceCollection services) services.AddGrpc().AddJsonTranscoding(); services.AddMvc(); + services.AddOpenApi(); + #region Secret services.AddSwaggerGen(c => { @@ -43,6 +45,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { + endpoints.MapOpenApi(); endpoints.MapGrpcService(); endpoints.MapGrpcService(); }); From bf2fcbb6bc4cdd091f180f050a442c023435d23e Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 4 Jun 2024 12:08:01 -0700 Subject: [PATCH 2/6] Set type on responses --- .../Internal/GrpcJsonTranscodingDescriptionProvider.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs index d614ffaa97bb..2e5f3783e724 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs @@ -81,13 +81,15 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, { ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } }, ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(responseType)), - StatusCode = 200 + StatusCode = 200, + Type = responseType }); apiDescription.SupportedResponseTypes.Add(new ApiResponseType { ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } }, ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(typeof(Google.Rpc.Status))), - IsDefaultResponse = true + IsDefaultResponse = true, + Type = typeof(Google.Rpc.Status) }); var explorerSettings = routeEndpoint.Metadata.GetMetadata(); if (explorerSettings != null) From 5823cc0b7a1f0864732059949f9c8c55f3a802f9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 5 Jun 2024 07:36:58 +0800 Subject: [PATCH 3/6] Simplify sandbox --- .../test/testassets/Sandbox/Sandbox.csproj | 2 ++ .../test/testassets/Sandbox/Startup.cs | 11 +++++++++-- .../test/testassets/Sandbox/greet.proto | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj index 146893e0dd85..2c932c3d364f 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Sandbox.csproj @@ -1,6 +1,8 @@ $(DefaultNetCoreTargetFramework) + true + $(NoWarn);1591 diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs index 93d34f61b220..0fcfeb8ecf21 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Extensions.Options; +using System.Reflection; using Microsoft.OpenApi.Models; namespace Server; @@ -10,7 +12,7 @@ public class Startup public void ConfigureServices(IServiceCollection services) { services.AddGrpc().AddJsonTranscoding(); - services.AddMvc(); + //services.AddMvc(); services.AddOpenApi(); @@ -18,6 +20,11 @@ public void ConfigureServices(IServiceCollection services) services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + xmlFilename = Path.Combine(AppContext.BaseDirectory, xmlFilename); + c.IncludeXmlComments(xmlFilename); + c.IncludeGrpcXmlComments(xmlFilename, includeControllerXmlComments: true); }); services.AddGrpcSwagger(); #endregion @@ -46,7 +53,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { endpoints.MapOpenApi(); - endpoints.MapGrpcService(); + //endpoints.MapGrpcService(); endpoints.MapGrpcService(); }); } diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/greet.proto b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/greet.proto index 4d121a7abcc3..cfb64498bdbf 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/greet.proto +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/greet.proto @@ -7,12 +7,15 @@ import "google/api/annotations.proto"; package greet; +// The greeting service definition. service Greeter { + // Sends a greeting. rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/v1/greeter/{name}" }; } + // Sends a greeting from someone. rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) { option (google.api.http) = { post: "/v1/greeter" @@ -22,15 +25,19 @@ service Greeter { } message HelloRequest { + // Name to greet. string name = 1; } message HelloRequestFrom { + // Name to greet. string name = 1; + // Greeting from. string from = 2; } message HelloReply { + // Greeting message. string message = 1; HelloReply nested = 2; } From 8d7fab19a707cd57754e472309dcfec1d551003b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 5 Jun 2024 13:24:07 +0800 Subject: [PATCH 4/6] Update --- AspNetCore.sln | 19 ------------------- src/Grpc/Grpc.slnf | 1 - 2 files changed, 20 deletions(-) diff --git a/AspNetCore.sln b/AspNetCore.sln index 898e31e3bc75..7eba541ea0a0 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1818,8 +1818,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentSample", "src\To EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorUnitedApp.Client", "src\Components\Samples\BlazorUnitedApp.Client\BlazorUnitedApp.Client.csproj", "{757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Grpc.OpenApi", "src\Grpc\JsonTranscoding\src\Microsoft.AspNetCore.Grpc.OpenApi\Microsoft.AspNetCore.Grpc.OpenApi.csproj", "{DE901A71-3EBD-4D25-B59F-F47C3F149361}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -10997,22 +10995,6 @@ Global {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x64.Build.0 = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.ActiveCfg = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.Build.0 = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|arm64.ActiveCfg = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|arm64.Build.0 = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x64.ActiveCfg = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x64.Build.0 = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x86.ActiveCfg = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Debug|x86.Build.0 = Debug|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|Any CPU.Build.0 = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|arm64.ActiveCfg = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|arm64.Build.0 = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x64.ActiveCfg = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x64.Build.0 = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x86.ActiveCfg = Release|Any CPU - {DE901A71-3EBD-4D25-B59F-F47C3F149361}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -11911,7 +11893,6 @@ Global {6A19D94D-2BC6-4198-BE2E-342688FDBA4B} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {D8F7091E-A2D1-4E81-BA7C-97EAE392D683} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} - {DE901A71-3EBD-4D25-B59F-F47C3F149361} = {151E6F9E-107B-4DDC-A2B1-95115801FD14} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/src/Grpc/Grpc.slnf b/src/Grpc/Grpc.slnf index ec8b230cb98a..23e31f454c88 100644 --- a/src/Grpc/Grpc.slnf +++ b/src/Grpc/Grpc.slnf @@ -8,7 +8,6 @@ "src\\Grpc\\Interop\\test\\testassets\\InteropWebsite\\InteropWebsite.csproj", "src\\Grpc\\JsonTranscoding\\perf\\Microsoft.AspNetCore.Grpc.Microbenchmarks\\Microsoft.AspNetCore.Grpc.Microbenchmarks.csproj", "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.JsonTranscoding\\Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj", - "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.OpenApi\\Microsoft.AspNetCore.Grpc.OpenApi.csproj", "src\\Grpc\\JsonTranscoding\\src\\Microsoft.AspNetCore.Grpc.Swagger\\Microsoft.AspNetCore.Grpc.Swagger.csproj", "src\\Grpc\\JsonTranscoding\\test\\Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests\\Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests.csproj", "src\\Grpc\\JsonTranscoding\\test\\Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests\\Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.csproj", From 76f78f83ccc2523255dd324a7cf5d791897de0ee Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 2 Jul 2024 16:24:47 +0800 Subject: [PATCH 5/6] Swagger++ --- src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs index 0fcfeb8ecf21..d0fe56fece07 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs +++ b/src/Grpc/JsonTranscoding/test/testassets/Sandbox/Startup.cs @@ -44,6 +44,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + c.SwaggerEndpoint("/openapi/v1.json", "OpenApi"); }); } #endregion From 2139eed9c145c3456af94358a82c3c37f25fbfa1 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 2 Jul 2024 18:24:24 +0800 Subject: [PATCH 6/6] Apply resolver and converter to JsonOptions --- .../GrpcJsonTranscodingServiceExtensions.cs | 24 +++++++++++++++++++ .../Internal/Json/JsonConverterHelper.cs | 23 +++++++++++------- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs index ceb7a2c0eb82..65084294cbc0 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs @@ -6,6 +6,8 @@ using Grpc.Shared; using Microsoft.AspNetCore.Grpc.JsonTranscoding; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Binding; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; +using Microsoft.AspNetCore.Http.Json; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -27,6 +29,7 @@ public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder buil builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IServiceMethodProvider<>), typeof(JsonTranscodingServiceMethodProvider<>))); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, GrpcJsonTranscodingOptionsSetup>()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, JsonOptionsSetup>()); builder.Services.TryAddSingleton(); return builder; @@ -63,4 +66,25 @@ public void Configure(GrpcJsonTranscodingOptions options) options.DescriptorRegistry = _descriptorRegistry; } } + + private sealed class JsonOptionsSetup : IConfigureOptions + { + private readonly DescriptorRegistry _descriptorRegistry; + private readonly GrpcJsonTranscodingOptions _transcodingOptions; + + public JsonOptionsSetup(DescriptorRegistry descriptorRegistry, IOptions transcodingOptions) + { + _descriptorRegistry = descriptorRegistry; + _transcodingOptions = transcodingOptions.Value; + } + + public void Configure(JsonOptions options) + { + ArgumentNullException.ThrowIfNull(options); + + var context = new JsonContext(_transcodingOptions.JsonSettings, _transcodingOptions.TypeRegistry, _descriptorRegistry); + + JsonConverterHelper.ApplyConverterAndTypeInfoSerializerOptions(options.SerializerOptions, context); + } + } } diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/JsonConverterHelper.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/JsonConverterHelper.cs index 0f5ebefdce18..95e56c042eff 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/JsonConverterHelper.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/JsonConverterHelper.cs @@ -37,17 +37,26 @@ internal static JsonSerializerOptions CreateSerializerOptions(JsonContext contex // For streaming to work, indenting must be disabled when streaming. var writeIndented = !isStreamingOptions ? context.Settings.WriteIndented : false; - var typeInfoResolver = JsonTypeInfoResolver.Combine( - new MessageTypeInfoResolver(context), - new DefaultJsonTypeInfoResolver()); - var options = new JsonSerializerOptions { WriteIndented = writeIndented, NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - TypeInfoResolver = typeInfoResolver + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + + ApplyConverterAndTypeInfoSerializerOptions(options, context); + + return options; + } + + internal static void ApplyConverterAndTypeInfoSerializerOptions(JsonSerializerOptions options, JsonContext context) + { + var typeInfoResolver = JsonTypeInfoResolver.Combine( + new MessageTypeInfoResolver(context), + options.TypeInfoResolver ?? new DefaultJsonTypeInfoResolver()); + + options.TypeInfoResolver = typeInfoResolver; + options.Converters.Add(new NullValueConverter()); options.Converters.Add(new ByteStringConverter()); options.Converters.Add(new Int64Converter(context)); @@ -56,8 +65,6 @@ internal static JsonSerializerOptions CreateSerializerOptions(JsonContext contex options.Converters.Add(new JsonConverterFactoryForEnum(context)); options.Converters.Add(new JsonConverterFactoryForWrappers(context)); options.Converters.Add(new JsonConverterFactoryForWellKnownTypes(context)); - - return options; } internal static Type GetFieldType(FieldDescriptor descriptor)