ASP.NET core server, AllowSynchronousIO
is set to false
new WebHostBuilder()
.UseKestrel(options =>
{
options.AllowSynchronousIO = false;
})
In the action, it outputs a JsonResult
public async Task<IActionResult> SanityCheck()
{
Dictionary<string, string> dic = await GetDic();
return this.Json(dic);
}
And it ends with an exception
System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
Can't I return a JsonResult with AllowSynchronousIO=false
?
You might have the following problem: https://github.com/aspnet/AspNetCore/issues/8302
And you can find more info here: https://github.com/aspnet/AspNetCore/issues/7644
A workaround until the issue is being solved is to allow Synchronous IO. Put this in Startup.cs for either Kestrel or IIS:
public void ConfigureServices(IServiceCollection services)
{
// If using Kestrel:
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
// If using IIS:
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
}
When the exception is thrown in code you cannot control, and you have no other choice than to enable AllowSynchronousIO
, it is best to enable it for specific requests, instead of globally.
In the GitHub issue announcing this feature, the following workaround is suggested:
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
You can create a simple middleware function to apply this to specific requests:
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/some-endpoint-that-needs-sync-io"))
{
var syncIoFeature = context.Features.Get<IHttpBodyControlFeature>();
if (syncIoFeature != null)
{
syncIoFeature.AllowSynchronousIO = true;
}
}
await next();
})
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With