I managed to find my own unique version of this problem with the following middleware (.NET 6.0):
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Response.StatusCode = 200;
using (var writer = new StreamWriter(context.Response.Body))
{
await writer.WriteLineAsync("Done!");
return;
}
}
I spent a long time staring at this until I realised what the stack trace was telling me:
System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.Write(ReadOnlySpan`1 buffer)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Dispose(Boolean disposing)
at System.IO.TextWriter.Dispose()
The important line here was the System.IO.TextWriter.Dispose()
: this is causing the resulting flush and lower-level write to be called synchronously.
Fortunately, StreamWriter
implements IAsyncDisposable
so it's easy to solve it by adding await
before using
:
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Response.StatusCode = 200;
await using (var writer = new StreamWriter(context.Response.Body))
{
await writer.WriteLineAsync("Done!");
return;
}
}
Hopefully this helps someone not waste as much time as I did.