Geta / geta-notfoundhandler

The popular NotFound handler for ASP.NET Core and Optimizely, enabling better control over your 404 page in addition to allowing redirects for old URLs that no longer works.
Apache License 2.0
20 stars 16 forks source link

InvalidOperationException: The response cannot be cleared, it has already started sending. #101

Closed andreas-valtech closed 11 months ago

andreas-valtech commented 1 year ago

We are using CMS 12 (on .NET 7) with the 5.0.6 version of Geta.NotFoundHandler.Optimizely and we have started seeing the following error in our logs for the path /apple-touch-icon-precomposed.png.

We are probably fixing the issue by just creating the file at that location but the same error is happening for any path with extension in the root of the site which would normally trigger a 404. It seems it is trying to clear the response which is not allowed:

Message: The response cannot be cleared, it has already started sending. Exception type: System.InvalidOperationException |   Failed method: Geta.NotFoundHandler.Infrastructure.Web.HttpContextExtensions.Redirect

System.InvalidOperationException: at Microsoft.AspNetCore.Http.ResponseExtensions.Clear (Microsoft.AspNetCore.Http.Extensions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60) at Geta.NotFoundHandler.Infrastructure.Web.HttpContextExtensions.Redirect (Geta.NotFoundHandler, Version=5.0.6.0, Culture=neutral, PublicKeyToken=null) at Geta.NotFoundHandler.Core.RequestHandler.Handle (Geta.NotFoundHandler, Version=5.0.6.0, Culture=neutral, PublicKeyToken=null) at Geta.NotFoundHandler.Infrastructure.Initialization.NotFoundHandlerMiddleware+d2.MoveNext (Geta.NotFoundHandler, Version=5.0.6.0, Culture=neutral, PublicKeyToken=null) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at Microsoft.AspNetCore.Builder.StatusCodePagesExtensions+<>cDisplayClass7_0+<b0>d.MoveNext (Microsoft.AspNetCore.Diagnostics, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware+d3.MoveNext (Microsoft.AspNetCore.Diagnostics, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at EPiServer.Cms.Shell.UI.Internal.RegisterAdminUserMiddleware+d5.MoveNext (EPiServer.Cms.Shell.UI, Version=12.22.5.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl+<gAwaited|8_0>d.MoveNext (Microsoft.AspNetCore.Diagnostics, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)

erdihu commented 1 year ago

It seems that adding if (context.Response.HasStarted) return; on top of the Redirect extension method resolves the problem but I am not sure why it happens in the first place.

https://github.com/Geta/geta-notfoundhandler/blob/2ed3c5f746d5497fd9f0a6432aa453935e2b8a9e/src/Geta.NotFoundHandler/Infrastructure/Web/HttpContextExtensions.cs#L48

erdihu commented 1 year ago

Another possible solution is to compare whether the NewUrl and OldUrl is same in the following method. Because in that case, we are handling the already redirected response (which has also a 404 response code), which is the default 404 page defined in a custom INotFoundHandler.

https://github.com/Geta/geta-notfoundhandler/blob/2ed3c5f746d5497fd9f0a6432aa453935e2b8a9e/src/Geta.NotFoundHandler/Core/RequestHandler.cs#L128

andreas-valtech commented 11 months ago

After decompiling and putting more logging in production version I found the cause which is not a bug. We had put the line:

app.UseStatusCodePagesWithReExecute("/error/{0}/");

above the lines to use the middleware:

app.UseCustomNotFoundHandler(); app.UseOptimizelyNotFoundHandler();

That seemed to cause it to fail for physical paths (ending in an extension) that did not exist.