函数执行后发生未处理的主机错误

问题描述 投票:0回答:1

我编写了一个 Azure 函数,用于在 HTTP 触发器上下载 Blob。当我在本地调试此函数时,代码执行会遍历整个函数,经过返回值,然后在实际发送返回值之前抛出一个模糊的错误。

这是函数:

public class GetBlob
    {
        private readonly IUpgradeDataRepositoryFactory _upgradeDataRepository;
        private readonly IAuthorizationHelper _authorizationHelper;

        public GetBlob(IUpgradeDataRepositoryFactory upgradeDataRepository, IAuthorizationHelper authorizationHelper){
            _upgradeDataRepository = upgradeDataRepository;
            _authorizationHelper = authorizationHelper;
        }

        [FunctionName("GetBlob")]
        public HttpResponseMessage Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "GetBlob/{id}")] HttpRequest req,
            string id, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            var clientIp = (req.Headers["X-Forwarded-For"].FirstOrDefault() ?? "").Split(new char[] { ':' }).FirstOrDefault();

            try{
                _authorizationHelper.AssertRequestIsAuthorized(req);

                var blobInfo = _upgradeDataRepository.UpdateDataItemRepository.Query(q => q.BlobName == id).FirstOrDefault();
                if(blobInfo != null){
                    using (var blobStream = GetBlobStream(id))
                    {
                        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
                        result.Content = new StreamContent(blobStream);
                        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                        result.Content.Headers.ContentDisposition.FileName = blobInfo.BlobFriendlyName;
                        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                        log.LogInformation(string.Format("Returning Update Blob: {0} to ip: {1}", id, clientIp));
                        return result;
                    }
                }
                else
                {
                    log.LogInformation(string.Format("Could not find update item record for blob id: {0} Ip: {1}", id, clientIp));
                    return new HttpResponseMessage(HttpStatusCode.NotFound);
                }
            }
            catch (AuthorizationException){
                log.LogInformation("Authorization failed");
                var message = new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.Unauthorized
                };
                throw new HttpResponseException(message);
            }
            catch (Exception ex){
                log.LogError(string.Format("Error getting update blob: {0} for IP: {1}", id, clientIp));
                log.LogError(ex.ToString());
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
            finally{
                log.LogInformation(string.Format("Exiting Get Update Blob: {0} for ip: {1}", id, clientIp));
            }
        }

        private static CloudStorageAccount GetStorageAccount()
        {
            var connParameter= "StorageAccountConnectionString";
            var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
            return CloudStorageAccount.Parse(connectionString);       
        }

        private static string GetContainerName(){
            var containerParameter= "UpdateBlobContainerName";
            var containerName = System.Environment.GetEnvironmentVariable($"{containerParameter}");
            return containerName;
        }


        private static Stream GetBlobStream(string id)
        {
            CloudStorageAccount account = GetStorageAccount();
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference(GetContainerName());
            var blob = container.GetBlobReference(id);

            return blob.OpenReadAsync().Result;
        }
    }

(注 1:这是使用 HttpResponseMessage,因为它正在替换旧的 API,并且需要与旧代码集成)

(注 2:虽然我还没有共享 AuthenticationHelper 或 UpgradeDataRepository 的代码,但这两个对象都已在单独的工作函数中成功使用和测试。UpgradeDataRepository 连接到 Azure SQL 数据库并查询一些信息 - 这种情况正在发生在工作函数和本函数中都成功。但是,工作函数不访问 blob 存储,因此错误一定与使用 blob 存储有关。)

这是终端输出:

[2024-03-27T19:04:34.288Z] Executing 'GetBlob' (Reason='This function was programmatically called via the host APIs.', Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e)
[2024-03-27T19:04:34.300Z] C# HTTP trigger function processed a request.
[2024-03-27T19:04:41.904Z] Returning Update Blob: 729ee33f-e283-420a-b898-648718139e3a to ip: 
[2024-03-27T19:04:41.906Z] Exiting Get Update Blob: 729ee33f-e283-420a-b898-648718139e3a for ip:
[2024-03-27T19:04:41.907Z] Executed 'GetBlob' (Succeeded, Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e, Duration=7650ms)
[2024-03-27T19:04:41.915Z] An unhandled host error has occurred.
[2024-03-27T19:04:41.916Z] Microsoft.WindowsAzure.Storage: Object reference not set to an instance of an object.

请注意消息“执行‘GetBlob’(成功)”发生在我的函数执行之外/之后,随后是错误消息。退货未到达我正在测试的应用程序。

错误消息很模糊,我的调试器没有捕获发生此错误的步骤,但它肯定是在执行显示的其他所有内容之后发生的。我什至检查了 blob 是否已成功下载并附加到响应中。

我目前唯一的猜测是版本不匹配?以防万一,这也是我的项目文件:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.14">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.14" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.14">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

更新:我尝试用 Azure.Storage.Blobs 替换 Microsoft.Windows.Azure:

private static BlobServiceClient GetStorageAccount()
        {
            var connParameter= "StorageAccountConnectionString";
            var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
            return new BlobServiceClient(connectionString);           
        }

private static Stream GetBlobStream(string id)
        {
            BlobServiceClient account = GetStorageAccount();
            BlobContainerClient container = account.GetBlobContainerClient(GetContainerName());
            var blob = container.GetBlobClient(id);

            return blob.OpenReadAsync().Result;
        }

现在,在一切似乎都成功执行之后,我在终端中收到了略有不同的错误:

[2024-03-28T12:09:20.144Z] Executed 'GetBlob' (Succeeded, Id=08567df1-e23f-4973-a9e5-a9750f0ff9ee, Duration=4304ms)
[2024-03-28T12:09:20.322Z] An unhandled host error has occurred.
[2024-03-28T12:09:20.323Z] System.Private.CoreLib: Value cannot be null. (Parameter 'buffer').
c# azure azure-functions azure-blob-storage .net-6.0
1个回答
0
投票

令人惊讶的是,本例中的罪魁祸首是 using 语句。

我将方法更改为同步运行并删除了 using 语句。我不确定为什么会这样,但该功能现在下载没有问题。我只能猜测这里发生了什么,但我认为这会强制 blob 下载进入阻塞操作,确保该对象可供响应对象使用。我很好奇是否有人知道为什么这有效。

                if(blobInfo != null){
                    var blob = GetBlobClient(id);
                    var blobStream = blob.OpenReadAsync().Result;
                    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
                    result.Content = new StreamContent(blobStream);
                    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                    result.Content.Headers.ContentDisposition.FileName = blobInfo.BlobFriendlyName;
                    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                    log.LogInformation(string.Format("Returning Update Blob: {0} to ip: {1}", id, clientIp));
                    return result;
                }
© www.soinside.com 2019 - 2024. All rights reserved.