ASP.NET Core:不允许进行同步操作。调用WriteAsync或将AllowSynchronousIO设置为true

Mr.*_*oor 16 asp.net-mvc

ASP.NET核心服务器,AllowSynchronousIO设置为false

        new WebHostBuilder()
        .UseKestrel(options =>
        {
            options.AllowSynchronousIO = false;
        })
Run Code Online (Sandbox Code Playgroud)

在动作中,它输出一个JsonResult

    public async Task<IActionResult> SanityCheck()
    {
        Dictionary<string, string> dic = await GetDic();

        return this.Json(dic);
    }
Run Code Online (Sandbox Code Playgroud)

它以一个异常结束

System.InvalidOperationException:不允许进行同步操作。调用WriteAsync或将AllowSynchronousIO设置为true。

我不能用返回JsonResult AllowSynchronousIO=false吗?

最好的祝福

Joh*_*ica 37

我设法使用以下中间件(.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我花了很长时间盯着这个,直到我意识到堆栈跟踪告诉我什么:

System.InvalidOperationException:不允许同步操作。改为调用 WriteAsync 或将 AllowSynchronousIO 设置为 true。

在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] 缓冲区,Int32 偏移量,Int32 计数)

在 System.IO.Stream.Write(ReadOnlySpan`1 缓冲区)

在System.IO.StreamWriter.Flush(布尔flushStream,布尔flushEncoder)

在 System.IO.StreamWriter.Dispose(布尔处置)

在 System.IO.TextWriter.Dispose()

这里重要的一行是System.IO.TextWriter.Dispose():这导致同步调用生成的刷新和较低级别的写入。

幸运的是,StreamWriter实现了,所以很容易通过添加beforeIAsyncDisposable来解决它:awaitusing

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助别人不要像我一样浪费太多时间。

  • 是的 - 确实在这里节省了一些时间!谢谢 :) (5认同)

小智 22

您可能遇到以下问题:https : //github.com/aspnet/AspNetCore/issues/8302

您可以在这里找到更多信息:https : //github.com/aspnet/AspNetCore/issues/7644

解决问题之前的解决方法是允许同步IO。将其放入用于Kestrel或IIS的Startup.cs中:

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;
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法异步发回 JsonResult?我认为这将是比允许在服务器上同步操作更好的解决方案。 (2认同)

T-m*_*oty 11

根据Mark Lagendijk回答,我通过属性以声明方式应用了该逻辑:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AllowSynchronousIOAttribute : ActionFilterAttribute
{
    public AllowSynchronousIOAttribute()
    {
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
        if (syncIOFeature != null)
        {
            syncIOFeature.AllowSynchronousIO = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

只需在操作方法或整个控制器上使用它即可启用它:

[AllowSynchronousIO]
public IActionResult DownloadSynchronous()
{
    return Something();
}
Run Code Online (Sandbox Code Playgroud)


小智 8

我的单元测试有这个问题。我不得不将我的 TestServer 更新为 AlloSynchronousIO

Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
Server.AllowSynchronousIO = true;
Run Code Online (Sandbox Code Playgroud)


小智 6

在配置服务中添加以下代码。这对我有用

 services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

            // If using IIS:
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });
Run Code Online (Sandbox Code Playgroud)


Mar*_*ijk 5

当异常在您无法控制的代码中抛出时,除了启用之外别无选择AllowSynchronousIO,最好为特定请求启用它,而不是全局启用它。

在宣布此功能的GitHub 问题中,建议使用以下解决方法:

var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
    syncIOFeature.AllowSynchronousIO = true;
}
Run Code Online (Sandbox Code Playgroud)

您可以创建一个简单的中间件函数来将其应用于特定请求:

app.Use(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();
})
Run Code Online (Sandbox Code Playgroud)