dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.23k stars 9.96k forks source link

SwaggerUI broken by primitive return values + endpoint filters #52128

Open erikhejl opened 10 months ago

erikhejl commented 10 months ago

Is there an existing issue for this?

Describe the bug

A combination of returning primitives from a minimal API delegate and adding an endpoint filter will cause the SwaggerUI page to crash. Primitive return values and endpoint filters do not cause this problem individually. This is in an application targeting .NET Core 7.

Expected Behavior

SwaggerUI page renders.

Steps To Reproduce

app.MapGet("/", () => { return 1; }).AddEndpointFilter(async (context, nextFilter) => await nextFilter(context));

Exceptions (if any)

ArgumentException: Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'System.Threading.Tasks.ValueTask`1[System.Object] WrapObjectAsValueTask(System.Object)' (Parameter 'arg0')

System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, string methodParamName, string argumentParamName, int index)
System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0)
Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateFilterPipeline(MethodInfo methodInfo, Expression targetExpression, RequestDelegateFactoryContext factoryContext, Expression<Func<HttpContext, object>> targetFactory)
Microsoft.AspNetCore.Http.RequestDelegateFactory.CreateTargetableRequestDelegate(MethodInfo methodInfo, Expression targetExpression, RequestDelegateFactoryContext factoryContext, Expression<Func<HttpContext, object>> targetFactory)
Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(Delegate handler, RequestDelegateFactoryOptions options, RequestDelegateMetadataResult metadataResult)
Microsoft.AspNetCore.Routing.RouteEndpointDataSource.CreateRouteEndpointBuilder(RouteEntry entry, RoutePattern groupPrefix, IReadOnlyList<Action<EndpointBuilder>> groupConventions, IReadOnlyList<Action<EndpointBuilder>> groupFinallyConventions)
Microsoft.AspNetCore.Routing.RouteEndpointDataSource.get_Endpoints()
Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.CreateEndpointsUnsynchronized()
Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.EnsureEndpointsInitialized()
Microsoft.AspNetCore.Routing.DataSourceDependentCache<T>.Initialize()
System.Threading.LazyInitializer.EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher..ctor(EndpointDataSource dataSource, Lifetime lifetime, Func<MatcherBuilder> matcherBuilderFactory)
Microsoft.AspNetCore.Routing.Matching.DfaMatcherFactory.CreateMatcher(EndpointDataSource dataSource)
Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.InitializeCoreAsync()
Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatcher|8_0(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task<Matcher> matcherTask)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

.NET Version

8.0.100

Anything else?

.NET SDK: Version: 8.0.100 Commit: 57efcf1350 Workload version: 8.0.100-manifests.8d38d0cc

Runtime Environment: OS Name: Windows OS Version: 10.0.22621 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.100\

.NET workloads installed: Workload version: 8.0.100-manifests.8d38d0cc [wasm-tools-net6] Installation Source: VS 17.8.34309.116 Manifest Version: 8.0.0/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.net6\8.0.0\WorkloadManifest.json Install Type: Msi

Host: Version: 8.0.0 Architecture: x64 Commit: 5535e31a71

.NET SDKs installed: 8.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

ryanpeters-MSFT commented 5 months ago

Has a fix been found for this? This sample code will fail, but changing the X and Y properties to string will work fine:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/addsmallnumbers", (AddRequest request) =>
{
    return request.X + request.Y;

}).AddEndpointFilter(async (ctx, next) => 
{
    return await next(ctx);
});

app.Run();

public class AddRequest
{
    public int X { get; set; } = default;
    public int Y { get; set; } = default;
}
erikhejl commented 5 months ago

It's still broken as of version 8.0.4. You can work around it by casting as (object), but this will defeat reflection based swagger generation done.

shanerogers commented 3 months ago

I just downloaded the lastest .net 8 sdk... I still get this issue..

captainsafia commented 1 month ago

I'll try to take a look at this bug and see if we can slot it in for servicing.

In the meantime, you can try seeing if compiling your application using the source generator for minimal APIs will work for you. It doesn't run into the same conversion bug we are seeing here with the Linq expressions we generated.

<PropertyGroup>
    <EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
</PropertyGroup>
captainsafia commented 1 month ago

This is fixed now in .NET 10 Preview 1 (we've already branched for .NET 9). .NET 10 preview builds aren't available yet. I'd like to wait until they are, have folks try them out to validate the fix, then backport to 8.0/9.0 once we've confirmed that this fix resolves the issue.

UdiAzulay commented 3 weeks ago

The issue only occur if the method that return int result has also an endpoint filter, without the filter, it works. Please patch a fix for .Net 8 ASAP (as it's the only available stable release now)