CarterCommunity / Carter

Carter is framework that is a thin layer of extension methods and functionality over ASP.NET Core allowing code to be more explicit and most importantly more enjoyable.
MIT License
2.17k stars 177 forks source link

IResponseNegotiator not terminating response and falling through to ExecuteReturnAsync.WriteJsonResponseAsync #370

Open toddsmith-adsk opened 6 days ago

toddsmith-adsk commented 6 days ago

I found that the default middleware in .NET 9 Minimal API is executing the following code below. When using IResponseNegotiator if I don't return an IResult object from my method it falls through to the WriteJsonResponseAsync call and then throws an exception about headers being read-only.

https://github.com/dotnet/aspnetcore/blob/1770dcf4e81872395cc4d3b3b3efbaef91f8020a/src/Shared/RouteHandlers/ExecuteHandlerHelper.cs#L27

   public static Task ExecuteReturnAsync(object obj, HttpContext httpContext, JsonTypeInfo<object> jsonTypeInfo)
    {
        // Terminal built ins
        if (obj is IResult result)
        {
            return result.ExecuteAsync(httpContext);
        }
        else if (obj is string stringValue)
        {
            SetPlaintextContentType(httpContext);
            return httpContext.Response.WriteAsync(stringValue);
        }
        else
        {
            // Otherwise, we JSON serialize when we reach the terminal state
            return WriteJsonResponseAsync(httpContext.Response, obj, jsonTypeInfo);
        }
    }

So instead of returning

return response.Negotiate(data);

I have to wrap the response in an IResult wrapper so that it hits the 1st conditional block in ExecuteReturnAsync.

return Results.Extensions.Negotiated(response.Negotiate(data));
public static class NegotiatedResultExtensions
{
    public static IResult Negotiated(this IResultExtensions _, Task obj)
    {
        return new NegotiatedResult(obj);
    }

    private class NegotiatedResult : IResult
    {
        private readonly Task _item;

        public NegotiatedResult(Task item)
        {
            _item = item;
        }

        public Task ExecuteAsync(HttpContext httpContext)
        {
            return _item;
        }
    }
jchannon commented 6 days ago

I assume this is an issue only for IAsyncEnumerable

On Sat, 23 Nov 2024 at 19:45, toddsmith-adsk @.***> wrote:

I found that the default middleware in .NET 9 Minimal API is executing the following code below. When using IResponseNegotiator if I don't return an IResult object from my method it falls through to the WriteJsonResponseAsync call and then throws an exception about headers being read-only.

https://github.com/dotnet/aspnetcore/blob/1770dcf4e81872395cc4d3b3b3efbaef91f8020a/src/Shared/RouteHandlers/ExecuteHandlerHelper.cs#L27

public static Task ExecuteReturnAsync(object obj, HttpContext httpContext, JsonTypeInfo jsonTypeInfo) { // Terminal built ins if (obj is IResult result) { return result.ExecuteAsync(httpContext); } else if (obj is string stringValue) { SetPlaintextContentType(httpContext); return httpContext.Response.WriteAsync(stringValue); } else { // Otherwise, we JSON serialize when we reach the terminal state return WriteJsonResponseAsync(httpContext.Response, obj, jsonTypeInfo); } }

So instead of returning

return response.Negotiate(data);

I have to wrap the response in an IResult wrapper so that it hits the 1st conditional block in ExecuteReturnAsync.

return Results.Extensions.Negotiated(response.Negotiate(data));

public static class NegotiatedResultExtensions { public static IResult Negotiated(this IResultExtensions _, Task obj) { return new NegotiatedResult(obj); }

private class NegotiatedResult : IResult
{
    private readonly Task _item;

    public NegotiatedResult(Task item)
    {
        _item = item;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
        return _item;
    }
}

— Reply to this email directly, view it on GitHub https://github.com/CarterCommunity/Carter/issues/370, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAZVJSVBQGMV3HDODGY7XD2CDLMPAVCNFSM6AAAAABSLMSAZSVHI2DSMVQWIX3LMV43ASLTON2WKOZSGY4DMNJRGI2TIMA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

toddsmith-adsk commented 6 days ago

I assume this is an issue only for IAsyncEnumerable

This is for a regular non-IAsyncEnumerable response.

jchannon commented 5 days ago

As far as I can see non IAsyncEnumerable is working fine.

For IAsyncEnumerable, Negotiate will just work with the default json negotiator, for example:

app.MapGet("/asyncenumerable", (HttpResponse res) =>
            {
                var enumerable = RangeAsync(10, 3);

                return res.Negotiate(enumerable);
            });

 static async IAsyncEnumerable<int> RangeAsync(int start, int count)
        {
            for (var i = 0; i < count; i++)
            {
                await Task.Delay(i);
                yield return start + i;
            }
        }

The above will return "[10,11,12]" as the body, if you want a negotiator to do something else then you can choose to do that yourself, for example,

public class AsyncEnumerableResponseNegotiator : IResponseNegotiator
{
    public bool CanHandle(MediaTypeHeaderValue accept)
    {
        return true;
    }

    public async Task Handle<T>(HttpRequest req, HttpResponse res, T model, CancellationToken cancellationToken)
    {
        var enumerable = (IAsyncEnumerable<int>)model;

        await foreach (var item in enumerable)
        {
            await res.WriteAsync(item.ToString(), cancellationToken);
        }
    }
}
toddsmith-adsk commented 4 days ago

I'm using Negotiate to return MemoryPack serialization and not json. I'll try and make a small demo.

toddsmith-adsk commented 3 days ago

Here is a demo of the issue

carter-memorypack-api

jchannon commented 3 days ago

Interestingly this doesn't cause an exception for me on macOS .NET9 and I get binary data in the response that I can view in a file

I did find this which maybe of interest but it produces the same data as your example https://github.com/Cysharp/MemoryPack/blob/main/src/MemoryPack.AspNetCoreMvcFormatter/MemoryPackOutputFormatter.cs

�����Item $0����Item $1����Item $2����Item $3����Item $4����Item $5����Item $6����Item $7����Item $8����Item $9����Item $10����Item $11����Item $12����Item $13����Item $14����Item $15����Item $16����Item $17����Item $18����Item $19����Item $20����Item $21����Item $22����Item $23����Item $24����Item $25����Item $26����Item $27����Item $28����Item $29����Item $30����Item $31����Item $32����Item $33����Item $34����Item $35����Item $36����Item $37����Item $38����Item $39����Item $40����Item $41����Item $42����Item $43����Item $44����Item $45����Item $46����Item $47����Item $48����Item $49����Item $50����Item $51����Item $52����Item $53����Item $54����Item $55����Item $56����Item $57����Item $58����Item $59����Item $60����Item $61����Item $62����Item $63����Item $64����Item $65����Item $66����Item $67����Item $68����Item $69����Item $70����Item $71����Item $72����Item $73����Item $74����Item $75����Item $76����Item $77����Item $78����Item $79����Item $80����Item $81����Item $82����Item $83����Item $84����Item $85����Item $86����Item $87����Item $88����Item $89����Item $90����Item $91����Item $92����Item $93����Item $94����Item $95����Item $96����Item $97����Item $98����Item $99����  Item $100����    Item $101����    Item $102����    Item $103����    Item $104����    Item $105����    Item $106����    Item $107����    Item $108����    Item $109����    Item $110����    Item $111����    Item $112����    Item $113����    Item $114����    Item $115����    Item $116����    Item $117����    Item $118����    Item $119����    Item $120����    Item $121����    Item $122����    Item $123����    Item $124����    Item $125����    Item $126����    Item $127����    Item $128����    Item $129����    Item $130����    Item $131����    Item $132����    Item $133����    Item $134����    Item $135����    Item $136����    Item $137����    Item $138����    Item $139����    Item $140����    Item $141����    Item $142����    Item $143����    Item $144����    Item $145����    Item $146����    Item $147����    Item $148����    Item $149����    Item $150����    Item $151����    Item $152����    Item $153����    Item $154����    Item $155����    Item $156����    Item $157����    Item $158����    Item $159����    Item $160����    Item $161����    Item $162����    Item $163����    Item $164����    Item $165����    Item $166����    Item $167����    Item $168����    Item $169����    Item $170����    Item $171����    Item $172����    Item $173����    Item $174����    Item $175����    Item $176����    Item $177����    Item $178����    Item $179����    Item $180����    Item $181����    Item $182����    Item $183����    Item $184����    Item $185����    Item $186����    Item $187����    Item $188����    Item $189����    Item $190����    Item $191����    Item $192����    Item $193����    Item $194����    Item $195����    Item $196����    Item $197����    Item $198����    Item $199����    Item $200����    Item $201����    Item $202����    Item $203����    Item $204����    Item $205����    Item $206����    Item $207����    Item $208����    Item $209����    Item $210����    Item $211����    Item $212����    Item $213����    Item $214����    Item $215����    Item $216����    Item $217����    Item $218����    Item $219����    Item $220����    Item $221����    Item $222����    Item $223����    Item $224����    Item $225����    Item $226����    Item $227����    Item $228����    Item $229����    Item $230����    Item $231����    Item $232����    Item $233����    Item $234����    Item $235����    Item $236����    Item $237����    Item $238����    Item $239����    Item $240����    Item $241����    Item $242����    Item $243����    Item $244����    Item $245����    Item $246����    Item $247����    Item $248����    Item $249����    Item $250����    Item $251����    Item $252����    Item $253����    Item $254����    Item $255����    Item $256����    Item $257����    Item $258����    Item $259����    Item $260����    Item $261����    Item $262����    Item $263����    Item $264����    Item $265����    Item $266����    Item $267����    Item $268����    Item $269����    Item $270����    Item $271����    Item $272����    Item $273����    Item $274����    Item $275����    Item $276����    Item $277����    Item $278����    Item $279����    Item $280����    Item $281����    Item $282����    Item $283����    Item $284����    Item $285����    Item $286����    Item $287����    Item $288����    Item $289����    Item $290����    Item $291����    Item $292����    Item $293����    Item $294����    Item $295����    Item $296����    Item $297����    Item $298����    Item $299����    Item $300����    Item $301����    Item $302����    Item $303����    Item $304����    Item $305����    Item $306����    Item $307����    Item $308����    Item $309����    Item $310����    Item $311����    Item $312����    Item $313����    Item $314����    Item $315����    Item $316����    Item $317����    Item $318����    Item $319����    Item $320����    Item $321����    Item $322����    Item $323����    Item $324����    Item $325����    Item $326����    Item $327����    Item $328����    Item $329����    Item $330����    Item $331����    Item $332����    Item $333����    Item $334����    Item $335����    Item $336����    Item $337����    Item $338����    Item $339����    Item $340����    Item $341����    Item $342����    Item $343����    Item $344����    Item $345����    Item $346����    Item $347����    Item $348����    Item $349����    Item $350����    Item $351����    Item $352����    Item $353����    Item $354����    Item $355����    Item $356����    Item $357����    Item $358����    Item $359����    Item $360����    Item $361����    Item $362����    Item $363����    Item $364����    Item $365����    Item $366����    Item $367����    Item $368����    Item $369����    Item $370����    Item $371����    Item $372����    Item $373����    Item $374����    Item $375����    Item $376����    Item $377����    Item $378����    Item $379����    Item $380����    Item $381����    Item $382����    Item $383����    Item $384����    Item $385����    Item $386����    Item $387����    Item $388����    Item $389����    Item $390����    Item $391����    Item $392����    Item $393����    Item $394����    Item $395����    Item $396����    Item $397����    Item $398����    Item $399����    Item $400����    Item $401����    Item $402����    Item $403����    Item $404����    Item $405����    Item $406����    Item $407����    Item $408����    Item $409����    Item $410����    Item $411����    Item $412����    Item $413����    Item $414����    Item $415����    Item $416����    Item $417����    Item $418����    Item $419����    Item $420����    Item $421����    Item $422����    Item $423����    Item $424����    Item $425����    Item $426����    Item $427����    Item $428����    Item $429����    Item $430����    Item $431����    Item $432����    Item $433����    Item $434����    Item $435����    Item $436����    Item $437����    Item $438����    Item $439����    Item $440����    Item $441����    Item $442����    Item $443����    Item $444����    Item $445����    Item $446����    Item $447����    Item $448����    Item $449����    Item $450����    Item $451����    Item $452����    Item $453����    Item $454����    Item $455����    Item $456����    Item $457����    Item $458����    Item $459����    Item $460����    Item $461����    Item $462����    Item $463����    Item $464����    Item $465����    Item $466����    Item $467����    Item $468����    Item $469����    Item $470����    Item $471����    Item $472����    Item $473����    Item $474����    Item $475����    Item $476����    Item $477����    Item $478����    Item $479����    Item $480����    Item $481����    Item $482����    Item $483����    Item $484����    Item $485����    Item $486����    Item $487����    Item $488����    Item $489����    Item $490����    Item $491����    Item $492����    Item $493����    Item $494����    Item $495����    Item $496����    Item $497����    Item $498����    Item $499����    Item $500����    Item $501����    Item $502����    Item $503����    Item $504����    Item $505����    Item $506����    Item $507����    Item $508����    Item $509����    Item $510����    Item $511����    Item $512����    Item $513����    Item $514����    Item $515����    Item $516����    Item $517����    Item $518����    Item $519����    Item $520����    Item $521����    Item $522����    Item $523����    Item $524����    Item $525����    Item $526����    Item $527����    Item $528����    Item $529����    Item $530����    Item $531����    Item $532����    Item $533����    Item $534����    Item $535����    Item $536����    Item $537����    Item $538����    Item $539����    Item $540����    Item $541����    Item $542����    Item $543����    Item $544����    Item $545����    Item $546����    Item $547����    Item $548����    Item $549����    Item $550����    Item $551����    Item $552����    Item $553����    Item $554����    Item $555����    Item $556����    Item $557����    Item $558����    Item $559����    Item $560����    Item $561����    Item $562����    Item $563����    Item $564����    Item $565����    Item $566����    Item $567����    Item $568����    Item $569����    Item $570����    Item $571����    Item $572����    Item $573����    Item $574����    Item $575����    Item $576����    Item $577����    Item $578����    Item $579����    Item $580����    Item $581����    Item $582����    Item $583����    Item $584����    Item $585����    Item $586����    Item $587����    Item $588����    Item $589����    Item $590����    Item $591����    Item $592����    Item $593����    Item $594����    Item $595����    Item $596����    Item $597����    Item $598����    Item $599����    Item $600����    Item $601����    Item $602����    Item $603����    Item $604����    Item $605����    Item $606����    Item $607����    Item $608����    Item $609����    Item $610����    Item $611����    Item $612����    Item $613����    Item $614����    Item $615����    Item $616����    Item $617����    Item $618����    Item $619����    Item $620����    Item $621����    Item $622����    Item $623����    Item $624����    Item $625����    Item $626����    Item $627����    Item $628����    Item $629����    Item $630����    Item $631����    Item $632����    Item $633����    Item $634����    Item $635����    Item $636����    Item $637����    Item $638����    Item $639����    Item $640����    Item $641����    Item $642����    Item $643����    Item $644����    Item $645����    Item $646����    Item $647����    Item $648����    Item $649����    Item $650����    Item $651����    Item $652����    Item $653����    Item $654����    Item $655����    Item $656����    Item $657����    Item $658����    Item $659����    Item $660����    Item $661����    Item $662����    Item $663����    Item $664����    Item $665����    Item $666����    Item $667����    Item $668����    Item $669����    Item $670����    Item $671����    Item $672����    Item $673����    Item $674����    Item $675����    Item $676����    Item $677����    Item $678����    Item $679����    Item $680����    Item $681����    Item $682����    Item $683����    Item $684����    Item $685����    Item $686����    Item $687����    Item $688����    Item $689����    Item $690����    Item $691����    Item $692����    Item $693����    Item $694����    Item $695����    Item $696����    Item $697����    Item $698����    Item $699����    Item $700����    Item $701����    Item $702����    Item $703����    Item $704����    Item $705����    Item $706����    Item $707����    Item $708����    Item $709����    Item $710����    Item $711����    Item $712����    Item $713����    Item $714����    Item $715����    Item $716����    Item $717����    Item $718����    Item $719����    Item $720����    Item $721����    Item $722����    Item $723����    Item $724����    Item $725����    Item $726����    Item $727����    Item $728����    Item $729����    Item $730����    Item $731����    Item $732����    Item $733����    Item $734����    Item $735����    Item $736����    Item $737����    Item $738����    Item $739����    Item $740����    Item $741����    Item $742����    Item $743����    Item $744����    Item $745����    Item $746����    Item $747����    Item $748����    Item $749����    Item $750����    Item $751����    Item $752����    Item $753����    Item $754����    Item $755����    Item $756����    Item $757����    Item $758����    Item $759����    Item $760����    Item $761����    Item $762����    Item $763����    Item $764����    Item $765����    Item $766����    Item $767����    Item $768����    Item $769����    Item $770����    Item $771����    Item $772����    Item $773����    Item $774����    Item $775����    Item $776����    Item $777����    Item $778����    Item $779����    Item $780����    Item $781����    Item $782����    Item $783����    Item $784����    Item $785����    Item $786����    Item $787����    Item $788����    Item $789����    Item $790����    Item $791����    Item $792����    Item $793����    Item $794����    Item $795����    Item $796����    Item $797����    Item $798����    Item $799����    Item $800����    Item $801����    Item $802����    Item $803����    Item $804����    Item $805����    Item $806����    Item $807����    Item $808����    Item $809����    Item $810����    Item $811����    Item $812����    Item $813����    Item $814����    Item $815����    Item $816����    Item $817����    Item $818����    Item $819����    Item $820����    Item $821����    Item $822����    Item $823����    Item $824����    Item $825����    Item $826����    Item $827����    Item $828����    Item $829����    Item $830����    Item $831����    Item $832����    Item $833����    Item $834����    Item $835����    Item $836����    Item $837����    Item $838����    Item $839����    Item $840����    Item $841����    Item $842����    Item $843����    Item $844����    Item $845����    Item $846����    Item $847����    Item $848����    Item $849����    Item $850����    Item $851����    Item $852����    Item $853����    Item $854����    Item $855����    Item $856����    Item $857����    Item $858����    Item $859����    Item $860����    Item $861����    Item $862����    Item $863����    Item $864����    Item $865����    Item $866����    Item $867����    Item $868����    Item $869����    Item $870����    Item $871����    Item $872����    Item $873����    Item $874����    Item $875����    Item $876����    Item $877����    Item $878����    Item $879����    Item $880����    Item $881����    Item $882����    Item $883����    Item $884����    Item $885����    Item $886����    Item $887����    Item $888����    Item $889����    Item $890����    Item $891����    Item $892����    Item $893����    Item $894����    Item $895����    Item $896����    Item $897����    Item $898����    Item $899����    Item $900����    Item $901����    Item $902����    Item $903����    Item $904����    Item $905����    Item $906����    Item $907����    Item $908����    Item $909����    Item $910����    Item $911����    Item $912����    Item $913����    Item $914����    Item $915����    Item $916����    Item $917����    Item $918����    Item $919����    Item $920����    Item $921����    Item $922����    Item $923����    Item $924����    Item $925����    Item $926����    Item $927����    Item $928����    Item $929����    Item $930����    Item $931����    Item $932����    Item $933����    Item $934����    Item $935����    Item $936����    Item $937����    Item $938����    Item $939����    Item $940����    Item $941����    Item $942����    Item $943����    Item $944����    Item $945����    Item $946����    Item $947����    Item $948����    Item $949����    Item $950����    Item $951����    Item $952����    Item $953����    Item $954����    Item $955����    Item $956����    Item $957����    Item $958����    Item $959����    Item $960����    Item $961����    Item $962����    Item $963����    Item $964����    Item $965����    Item $966����    Item $967����    Item $968����    Item $969����    Item $970����    Item $971����    Item $972����    Item $973����    Item $974����    Item $975����    Item $976����    Item $977����    Item $978����    Item $979����    Item $980����    Item $981����    Item $982����    Item $983����    Item $984����    Item $985����    Item $986����    Item $987����    Item $988����    Item $989����    Item $990����    Item $991����    Item $992����    Item $993����    Item $994����    Item $995����    Item $996����    Item $997����    Item $998����    Item $999

On Tue, 26 Nov 2024 at 18:29, toddsmith-adsk @.***> wrote:

Here is a demo of the issue

carter-memorypack-api https://github.com/toddsmith-adsk/carter-memorypack-api

— Reply to this email directly, view it on GitHub https://github.com/CarterCommunity/Carter/issues/370#issuecomment-2501542583, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAZVJWUQJGWHXDSZNLEIML2CSVYZAVCNFSM6AAAAABSLMSAZSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMBRGU2DENJYGM . You are receiving this because you commented.Message ID: @.***>

toddsmith-adsk commented 3 days ago

You tested both the / and /result endpoints and got the same result? The / endpoint is the one that should generate an error on my windows box running Visual Studio 2022.

jchannon commented 3 days ago

Yup. Both worked

On Wed, 27 Nov 2024 at 00:21, toddsmith-adsk @.***> wrote:

You tested both the / and /result endpoints and got the same result? The / endpoint is the one that should generate an error on my windows box running Visual Studio 2022.

— Reply to this email directly, view it on GitHub https://github.com/CarterCommunity/Carter/issues/370#issuecomment-2502167315, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAZVJS5HUEMVVBDJI7WUGL2CT67VAVCNFSM6AAAAABSLMSAZSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKMBSGE3DOMZRGU . You are receiving this because you commented.Message ID: @.***>