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