Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core : Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead

Tags:

asp.net-mvc

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 ?

like image 385
Mr.Wang from Next Door Avatar asked Dec 10 '17 01:12

Mr.Wang from Next Door


3 Answers

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;
    });
}
like image 99
hightech Avatar answered Nov 15 '22 09:11

hightech


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();
})
like image 18
Mark Lagendijk Avatar answered Nov 15 '22 07:11

Mark Lagendijk


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.

like image 11
DiplomacyNotWar Avatar answered Nov 15 '22 07:11

DiplomacyNotWar