Skip to content

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

Open
@toddsmith-adsk

Description

@toddsmith-adsk

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;
        }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions