首页 新闻 会员 周边

ASP.NET Core ViewComponent 报错:"Synchronous operations are disallowed"

0
悬赏园豆:30 [已解决问题] 解决于 2023-09-23 11:59

在一个 ASP.NET Core 项目的日志中发现下面的错误:

Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

异常堆栈如下:

Microsoft.AspNetCore.Server.Kestrel
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 Microsoft.AspNetCore.ResponseCaching.ResponseCachingStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.Write(String value)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.WriteToAsync(TextWriter writer, HtmlEncoder encoder)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewComponentResultExecutor.ExecuteAsync(ActionContext context, ViewComponentResult result)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewComponentResultExecutor.ExecuteAsync(ActionContext context, ViewComponentResult result)
问题补充:
  • 不想采用 AllowSynchronousIO = true 的方法
  • 异常是输出 IHtmlContent 时触发的(ViewBuffer.cs#L258
if (value.Value is IHtmlContent valueAsHtmlContent)
{
    valueAsHtmlContent.WriteTo(writer, encoder);
    await writer.FlushAsync();
    continue;
}

【更新】

通过 github issue 评论中提供的重现问题的代码发现,当 View Component 视图中要输出的字符串超过 16K 就会触发这个问题

public class HomeController : Controller
{
    public IActionResult Fail()
    {
        return ViewComponent(typeof(FailingViewComponent));
    }
}

public class FailingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        return new HtmlString(new string('X', 16385)); // 16384 is OK
    }
}
dudu的主页 dudu | 高人七级 | 园豆:30994
提问于:2023-03-01 07:58

异常是在 ViewBuffer.cs#L258 抛出的

dudu 1年前

ViewBuffer 也实现了 IHtmlContent 接口:ViewBuffer -> IHtmlContentBuilder -> IHtmlContentContainer -> IHtmlContent

dudu 1年前

value.ValueViewBufferValue.Value

dudu 1年前

EncodingWrapper 只实现了 IHtmlContentnew ViewBufferValue(new EncodingWrapper(unencoded)) 会触发这个问题

dudu 1年前
< >
分享
最佳答案
0

终于找到了原因!是 MemoryPoolHttpResponseStreamWriterFactory.cs#L28 中的 DefaultBufferSize 引起的

public const int DefaultBufferSize = 16 * 1024;

自己实现一个 CustomMemoryPoolHttpResponseStreamWriterFactory 修改 DefaultBufferSize 的值

public class CustomMemoryPoolHttpResponseStreamWriterFactory : IHttpResponseStreamWriterFactory
{
    public const int DefaultBufferSize = 32 * 1024;

    private readonly ArrayPool<byte> _bytePool;
    private readonly ArrayPool<char> _charPool;

    public CustomMemoryPoolHttpResponseStreamWriterFactory(
        ArrayPool<byte> bytePool,
        ArrayPool<char> charPool)
    {
        _bytePool = bytePool;
        _charPool = charPool;
    }

    public TextWriter CreateWriter(Stream stream, Encoding encoding)
    {
        return new HttpResponseStreamWriter(stream, encoding, DefaultBufferSize, _bytePool, _charPool);
    }
}

替换掉 MemoryPoolHttpResponseStreamWriterFactory

builder.Services.TryAddSingleton<IHttpResponseStreamWriterFactory, CustomMemoryPoolHttpResponseStreamWriterFactory>();

问题就解决了

// FailingViewComponent no longer failed
public class FailingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        // 16385 is OK with CustomMemoryPoolHttpResponseStreamWriterFactory
        return new HtmlString(new string('X', 16385)); 
    }
}
dudu | 高人七级 |园豆:30994 | 2023-03-01 20:35

有个变通的解决方法,可以将需要渲染的大于 16k 的 html 字符串分片处理

<div id="sidebar_news_content">
    foreach (var chuck in Model.Blog.SplitNews())
    {
        @Html.Raw(chuck);
    }    
</div>
public IEnumerable<string> SplitNews(int chunkSize = 16 * 1024)
{
    for (int i = 0; i < News.Length; i += chunkSize)
    {
        yield return News.Substring(i, Math.Min(chunkSize, News.Length - i));
    }
}
dudu | 园豆:30994 (高人七级) | 2023-03-04 21:34
其他回答(1)
0

valueAsHtmlContent.WriteTo(writer, encoder); 这个方法看看有没有异步写法

收获园豆:30
diudiu1 | 园豆:1031 (小虾三级) | 2023-03-01 10:52

这是 ASP.NET Core 的源码,没法动

支持(0) 反对(0) dudu | 园豆:30994 (高人七级) | 2023-03-01 11:05
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册