【问题标题】:"Synchronous operations are disallowed" again再次“不允许同步操作”
【发布时间】:2021-11-01 17:38:08
【问题描述】:

我已阅读主题:ASP.NET Core : Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead 但对于我的情况,我无法理解,如何解决它:

我有控制器方法:

    public async Task<IActionResult> Upload()
    {
        var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
        var reader = new MultipartReader(boundary.ToString(), HttpContext.Request.Body);

        var section = await reader.ReadNextSectionAsync();
        List<FileApi> uploadedFiles = new List<FileApi>();

        while (section != null)
        {
            ContentDispositionHeaderValue contentDisposition;
            var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

            if (hasContentDispositionHeader)
            {
                if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
                {
                    var fileDto = await _zendeskManageFileService.UploadFileAsync(contentDisposition.FileName.ToString(), section.Body);
                    uploadedFiles.Add(_mapper.Map<FileApi>(fileDto));
                }
                else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
                {
                    var errorNoFile = new ErrorResponse
                    {
                        Error = new Error
                        {
                            Code = "NoFile",
                            Message = $"Expected a file, no form data content",
                        }
                    };

                    return BadRequest(errorNoFile);
                }
            }
            section = await reader.ReadNextSectionAsync();
        }

        return Created("", uploadedFiles);
    }

当有 2 个或更多文件时,我在线上的第二个文件出现异常:

var fileDto = await _zendeskManageFileService.UploadFileAsync(contentDisposition.FileName.ToString(), section.Body);

UploadFileAsync 的代码如下:

    public async Task<FileDto> UploadFileAsync(string filename, Stream stream)
    {
        if (stream.Position != 0)
            stream.Position = 0;

        var upload = await _zendeskClient.Attachments.UploadAsync(filename, stream);
        FileDto fileDto = new FileDto
        {
            Id = upload.Id,
            ContentType = upload.Attachment.ContentType,
            Token = upload.Token,
            ContentUrl = upload.Attachment.ContentUrl,
            FileName = upload.Attachment.FileName,
            Size = upload.Attachment.Size
        };

        return fileDto;
    }

    public async Task<Upload> UploadAsync(
        string fileName, 
        Stream inputStream, 
        string token = null,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var attachmentResponse = await ExecuteRequest(async (client, canToken) => 
                await client.PostAsBinaryAsync(
                    UploadsResourceUri + $"?filename={fileName}&token={token}",
                    inputStream,
                    fileName,
                    canToken)
                .ConfigureAwait(false), 
                "UploadAsync",
                cancellationToken)
            .ThrowIfUnsuccessful("attachments#upload-files", HttpStatusCode.Created)
            .ReadContentAsAsync<UploadResponse>();

        return attachmentResponse.Upload;
    }

    public static async Task<HttpResponseMessage> PostAsBinaryAsync(
        this HttpClient client,
        string requestUri,
        Stream inputStream,
        string fileName,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        using (var stream = new MemoryStream())
        {
            inputStream.CopyTo(stream);

            using (var content = new ByteArrayContent(stream.ToArray()))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue("application/binary");

                return await client.PostAsync(
                        requestUri, 
                        content, 
                        cancellationToken)
                    .ConfigureAwait(false);
            }
        }
    }

    internal static async Task<T> ReadContentAsAsync<T>(
        this Task<HttpResponseMessage> response,
        JsonConverter converter = null)
        where T : class
    {
        var unwrappedResponse = await response;

        if (unwrappedResponse == null)
            return null;

        if (converter == null)
        {
            return await unwrappedResponse
                .Content
                .ReadAsAsync<T>();
        }

        return await unwrappedResponse
            .Content
            .ReadAsAsync<T>(converter);
    }

    protected async Task<HttpResponseMessage> ExecuteRequest(
        Func<HttpClient, CancellationToken, Task<HttpResponseMessage>> requestBodyAccessor,
        string scope,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        using (LoggerScope(Logger, scope))
        using (var client = ApiClient.CreateClient())
        {
            return await requestBodyAccessor(client, cancellationToken);
        }
    }

我尝试创建和使用属性来设置AllowSynchronousIO = true

[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;
        }
    }
}

但这无济于事。我的情况如何解决这个问题?

堆栈错误是:

System.InvalidOperationException:同步操作是 不允许。改为调用 ReadAsync 或将 AllowSynchronousIO 设置为 true。 在 Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 计数)在 Microsoft.AspNetCore.Server.IIS.Core.WrappingStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 计数)在 Microsoft.AspNetCore.WebUtilities.BufferedReadStream.EnsureBuffered(Int32 minCount) 在 Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.Read(字节 [] 缓冲区,Int32 偏移量,Int32 计数)在 System.IO.Stream.CopyTo(流目标,Int32 bufferSize)在 System.IO.Stream.CopyTo(流目的地)在 ZendeskApi.Client.Extensions.HttpRequestExtensions.PostAsBinaryAsync(HttpClient 客户端,字符串 requestUri,流 inputStream,字符串文件名, CancellationToken 取消令牌)在 C:\www\TMS\Libraries\ZendeskApi.Client\Extensions\HttpRequestExtensions.cs:line 75 在 ZendeskApi.Client.Resources.AttachmentsResource.c__DisplayClass4_0.d.MoveNext() 在 C:\www\TMS\Libraries\ZendeskApi.Client\Resources\AttachmentsResource.cs:line 45 --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 ZendeskApi.Client.Resources.AbstractBaseResource1.ExecuteRequest(Func3 requestBodyAccessor,字符串范围,CancellationToken 取消令牌)在 C:\www\TMS\Libraries\ZendeskApi.Client\Resources\AbstractBaseResource.cs:line 41 在 ZendeskApi.Client.Extensions.HttpResponseExtensions.ThrowIfUnsuccessful(预期任务1 response, String helpDocLink, Nullable1,字符串 helpDocLinkPrefix) 在 C:\www\TMS\Libraries\ZendeskApi.Client\Extensions\HttpResponseExtensions.cs:line 90 在 ZendeskApi.Client.Extensions.HttpResponseExtensions.ReadContentAsAsync[T](Task`1 响应,JsonConverter 转换器)在 C:\www\TMS\Libraries\ZendeskApi.Client\Extensions\HttpResponseExtensions.cs:line 45 在 ZendeskApi.Client.Resources.AttachmentsResource.UploadAsync(字符串 文件名、流输入流、字符串令牌、CancellationToken 取消令牌)在 C:\www\TMS\Libraries\ZendeskApi.Client\Resources\AttachmentsResource.cs:line 44 在 Tms.ZendeskCore.Services.ZendeskManageFileService.UploadFileAsync(字符串 文件名,流流)在 C:\www\TMS\Libraries\Tms.ZendeskCore\Services\ZendeskManageFileService.cs:line 24 在 Tms.WebApi.Controllers.Support.ManageFileApiController.Upload() 在 C:\www\TMS\Web\Tms.WebApi\Controllers\Support\ManageFileApiController.cs:line 86 在 Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper 映射器,ObjectMethodExecutor 执行器,对象控制器,Object[] 论据)在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Logged|12_1(ControllerActionInvoker 调用者)在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker 调用者,任务 lastTask,下一个状态,作用域范围,对象状态,布尔值 已完成)在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed 上下文)在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(状态& next, Scope& 范围, Object& 状态, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker 调用者,任务 lastTask,下一个状态,作用域范围,对象状态,布尔值 已完成)在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker 调用者,任务 lastTask,下一个状态,作用域范围,对象状态,布尔值 已完成)在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker 调用者)在 Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(端点 端点,任务 requestTask,ILogger 记录器)在 Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext 上下文)在 Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext 上下文)在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文)在 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)

标题 ======= Accept: / Accept-Encoding: gzip, deflate, br Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...7KlZaAycWieBAvkk2WUuTkI_UqNu9IzUhk0JAeLQkvU 缓存控制:无缓存连接:保持活动内容长度:114796 内容类型:multipart/form-data; 边界=--------------271301571241092277677258 主机: 本地主机:44373 用户代理:PostmanRuntime/7.28.3 邮递员令牌: 4fd12960-9bae-45f5-8ae1-e4a5080b887b

【问题讨论】:

  • 什么是堆栈跟踪?我的 猜测section.Body 流正在与同步 API 一起使用; section 上是否有管道 API?你能缓冲一下吗?等
  • @MarcGravell 添加
  • 嘿,“称之为”:p OK;所以:您要么需要执行以下操作之一:a) 使用管道 API 而不是 Stream,b) 手动缓冲数据,或 c) 启用同步 IO;有没有接受管道阅读器的上传 API?

标签: c# asynchronous file-upload


【解决方案1】:

inputStream.CopyTo(stream) 同步复制流。您需要使用 StreamContent 而不是 ByteArrayContent,或者异步复制流 (CopyToAsync)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-27
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2021-02-10
    相关资源
    最近更新 更多