OnStarting cannot be set because the response has already started.

See original GitHub issue

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I’m trying to recreate a feature from a legacy api that supported jsonp as a middleware in .net 7. What I have written appears to work fine when I initiate the request from the chrome browser but when using a node http client kestrel throws an invalidoperationexcpetion.

Expected Behavior

No matter the client, the middleware should modify the response without error.

Steps To Reproduce

i’ve registered this middleware at a few different spots in the pipeline and it doesn’t seem to matter.

#nullable enable
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace api.quirks;

public class JsonpMiddleware {
    private readonly RequestDelegate _next;

    public JsonpMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        var version = context.GetRequestedApiVersion()!;

        if (version > ApiVersion.Default) {
            await _next(context);

            return;
        }

        if (!context.Request.Query.TryGetValue("callback", out var callback)) {
            await _next(context);

            return;
        }

        if (!context.Response.HasStarted) {
            context.Response.OnStarting(() => {
                context.Response.ContentType = "text/javascript; charset=utf-8";

                return Task.CompletedTask;
            });
        }

        await context.Response.WriteAsync($"/**/ typeof {callback} === 'function' && {callback} (");
        await _next(context); // this is the line that throws
        await context.Response.WriteAsync(");");
        await context.Response.Body.FlushAsync();

        return;
    }
}

Exceptions (if any)

[ERR 10:31:42] (Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware) An unhandled exception has occurred while executing the request. 
System.InvalidOperationException: OnStarting cannot be set because the response has already started.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(String value)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.OnStarting(Func`2 callback, Object state)
   at Microsoft.AspNetCore.Mvc.ReportApiVersionsAttribute.OnActionExecuting(ActionExecutingContext context)
   at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at CorrelationId.CorrelationIdMiddleware.Invoke(HttpContext context, ICorrelationContextFactory correlationContextFactory)
   at api.quirks.JsonpMiddleware.InvokeAsync(HttpContext context) in .../JsonpMiddleware.cs:line 40
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
[WRN 10:31:42] (Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware) The response has already started, the error page middleware will not be executed. 

.NET Version

7.0.202

Anything else?

No response

Issue Analytics

  • State:closed
  • Created 6 months ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Tratchercommented, Mar 22, 2023

Try this:

var originalResponseStream = context.Response.Body;
using var stream = new MemoryStream();
context.Response.Body = stream;

await _next(context);

stream.Position = 0;
context.Response.Body = originalResponseStream;

await context.Response.WriteAsync($"/**/ typeof {callback} === 'function' && {callback} (");
await stream.CopyToAsync(context.Response.Body);
await context.Response.WriteAsync(");");
1reaction
Tratchercommented, Mar 22, 2023

You should not call WriteAsync and then call next. The next middleware isn’t expecting the response to have already started. If you need to wrap/modify the response body you’ll need to intercept the HttpResponse.Body Stream and do the modification inline or buffer and do it after next.

Read more comments on GitHub >

github_iconTop Results From Across the Web

OnStarting cannot be set because the response has ...
The cause behind it is, Signin Manager makes use of HTTPContext to create cookies. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.
Read more >
Response is null if middleware return AuthenticateResult. ...
InvalidOperationException : StatusCode cannot be set because the response has already started. at Microsoft.AspNetCore.Server.Kestrel.Core.
Read more >
Add Headers To A Response In ASP.NET 5
System.InvalidOperationException : Headers are read-only, response has already started. This exception occurs when our middleware takes the ...
Read more >
Manipulating response headers in ASP.Net Core
First comes the status code, then come the headers and then comes the body. So if Asp.Net Core has started to write even...
Read more >
Setting ASP.Net Cookies in Middleware
My searching led met to the Response.OnStarting method of the HttpResponse class. This method accepts a delegate to be invoked just before ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found