Minimal APIs: a C# union of IResult cases is serialized as JSON with 200 instead of executing the active case
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- csharp
- Ambito
- backend-api-design
Direzione di ricerca
Read RequestDelegateFactory, ExecuteHandlerHelper.ExecuteReturnAsync, the generated ExecuteReturnAsync, and EndpointMetadataPopulator; the issue identifies these as the return handling and metadata paths to inspect. Compare how they handle Results<T1,T2> with how a C# union exposes its active value, then run or add coverage for the reflection and Request Delegate Generator paths, endpoint filters, and OpenAPI metadata. Done means active IResult cases execute with their status and content type, and each case's response metadata reaches OpenAPI.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
When a Minimal API handler returns a C# union whose cases are IResult types, the framework does not execute the active case. It serializes the union as JSON with status 200. A 404 problem goes out as a 200, with no error and no warning.
It happens on both the reflection path and the Request Delegate Generator.
Expected Behavior
The same behavior as Results<Ok<UserDto>, ProblemHttpResult>: the active case is executed (here 404 application/problem+json), and each case's response metadata reaches OpenAPI.
If this shape is not meant to be supported, an analyzer warning or a startup error would be much better than a wrong status code at runtime.
Steps To Reproduce
using Microsoft.AspNetCore.Http.HttpResults;
var app = WebApplication.Create(args);
app.MapGet("/union/{ok:bool}", (bool ok) => ok
? (Outcome)TypedResults.Ok(new UserDto(1, "ann"))
: TypedResults.Problem(statusCode: 404));
app.MapGet("/results/{ok:bool}", Results<Ok<UserDto>, ProblemHttpResult> (bool ok) => ok
? TypedResults.Ok(new UserDto(1, "ann"))
: TypedResults.Problem(statusCode: 404));
app.Run();
public record UserDto(int Id, string Name);
public union Outcome(Ok<UserDto>, ProblemHttpResult);
GET /results/false (correct):
404 application/problem+json
{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.5","title":"Not Found","status":404}
GET /union/false (wrong):
200 application/json
{"problemDetails":{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.5","title":"Not Found","status":404},"contentType":"application/problem+json","statusCode":404}
The success case is wrong too. GET /union/true returns the Ok<T> wrapper instead of the DTO:
200 application/json
{"value":{"id":1,"name":"ann"},"statusCode":200}
Also:
- Endpoint filters get the union (
IUnion) as the return value, not anIResult. - OpenAPI shows one
200 application/jsonresponse whose schema isanyOf: [OkOfUserDto, ProblemHttpResult].
Exceptions (if any)
None. The wrong response is sent silently.
.NET Version
SDK 11.0.100, runtime 11.0.0. Same on 11.0.0-rc.3, and the same with <EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>.
Anything else?
Every check looks only at the declared return type, and a union type does not implement IResult:
RequestDelegateFactory:typeof(IResult).IsAssignableFrom(returnType), otherwise the JSON writer.ExecuteHandlerHelper.ExecuteReturnAsyncand the generatedExecuteReturnAsync:obj is IResult.EndpointMetadataPopulator:typeof(IEndpointMetadataProvider).IsAssignableFrom(returnType).
None of them look at IUnion.Value.
#66951 verified unions of data types as return types, but has no case with IResult cases. In #66857 it was suggested that users should eventually move to C# unions instead of Results<>, which makes this shape a natural thing to try. Related: #64599.
I am happy to send a PR once the expected behavior is agreed.
- Lingua principale
- C#
- Stelle
- 38.5k
- Fork
- 11.6k
- Merge medio
- 3g 1h
- PR unite (30g)
- 289
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di dotnet/aspnetcore
-
area-networking
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70545 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
area-infrastructure
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
dotnet/aspnetcore#70524 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[OpenTelemetry] Hosting replaces an Activity.DisplayName that the app set, when a route matchesApertaarea-hosting Regression
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70516 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
[SSE] ServerSentEventsResult.WrapEvents is missing [EnumeratorCancellation], so RequestAborted never reaches the app's generator on the IAsyncEnumerable<T> overloadForse già presa @bharatprajapati-coder l’ha presa 1 giorno fa. Apertaarea-minimal
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70419 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
[Blazor] DefaultInMemoryCircuitPersistenceProvider never disposes the retention CancellationTokenSource when a persist is rejected at capacityForse già presa @Vinoth2562000 l’ha presa 1 giorno fa. Apertaarea-blazor
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
dotnet/aspnetcore#70418 · 1 commento · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di dotnet/aspnetcore
Issue simili
-
bug emitter:client:csharp
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
microsoft/typespec#12572 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
needs-area-label
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 2 giorni
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 87/100
I maintainer di solito rispondono entro 1 giorno
-
priority:P2
Difficoltà 2/5 1-3 ore Idoneità per principianti 80/100