dicej / spin-dotnet-sdk

Experimental Spin SDK for .NET
2 stars 4 forks source link

[samples/aspdotnet] minimal apis with async result throw runtime Exception #19

Open bacongobbler opened 3 months ago

bacongobbler commented 3 months ago

Given a minimal API endpoint such as the following:

app.MapPost("/", async ([FromServices] IMyService myService) =>
{
    app.Logger.LogInformation("doing something");
    await myService.DoSomething();
    app.Logger.LogInformation("completed something");
    return "";
});

Results in an error at runtime:

Unhandled exception. System.AggregateException: One or more errors occurred. (JsonTypeInfo metadata for type 'System.Threading.Tasks.Task`1[System.String]' was not provided by TypeInfoResolver of type '[SpinHttpWorld.wit.exports.wasi.http.v0_2_0.AppJsonSerializerContext, Microsoft.AspNetCore.OpenApi.OpenApiJsonSchemaContext]'. If using source generation, ensure that all root types passed to the serializer have been annotated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.)

I am assuming that the async result from the request handler needs to be handled by WasiHttpServer before passing it along to the resolver.

dicej commented 3 months ago

Yeah, I ran into this yesterday. After some experimentation, I found that this worked, at least:

        app.MapGet(
            "/hellosql",
            async context =>
            {
                await HelloSql();
            }
        );

// ...
    private static async Task<string> HelloSql()
    {
        // ...
    }

That was inspired by https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-8.0#endpoint-defined-outside-of-programcs, along with https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.endpointroutebuilderextensions.mapget?view=aspnetcore-8.0#microsoft-aspnetcore-builder-endpointroutebuilderextensions-mapget(microsoft-aspnetcore-routing-iendpointroutebuilder-system-string-microsoft-aspnetcore-http-requestdelegate) and https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.requestdelegate?view=aspnetcore-8.0.

The Map* functions are heavily overloaded, and I'm guessing sometimes they need type hints in order to make sure you're using the right one, at least when NativeAOT is in the mix.

bacongobbler commented 3 months ago

That does appear to work. Thank you!

Here's the updated sample:

app.MapPost("/", async context =>
{
    var myService = context.RequestServices.GetRequiredService<IMyService>();
    app.Logger.LogInformation("doing something");
    await myService.DoSomething();
    app.Logger.LogInformation("completed something");
    context.Response.StatusCode = StatusCodes.Status204NoContent;
});
bacongobbler commented 3 months ago

I'll keep this open for now with a note to document this for others.