You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8独立模式Azure函数EventGridTrigger订阅配置问题

.NET 8独立模式Azure函数Event Grid触发器部署后订阅不可见的解决方法

排查与解决步骤

1. 确认函数部署状态

  • 登录Azure门户进入目标函数应用,查看「函数」列表,确认BlobTriggerFunction已成功部署并处于运行状态。若列表无此函数,前往「部署中心」查看日志,排查编译或发布失败原因。
  • 在「配置」→「常规设置」中,确认运行时堆栈为.NET 8,运行模式为「独立」。

2. 验证Event Grid扩展配置

独立模式下需确保host.json正确配置Event Grid扩展:

{
  "version": "2.0",
  "extensions": {
    "eventGrid": {
      "batchProcessing": false,
      "eventTypes": [
        "Microsoft.Storage.BlobCreated"
      ]
    }
  }
}

3. 检查权限与身份配置

  • 若使用系统托管身份,需为该身份分配「Event Grid订阅者」角色,确保其有权接收事件。
  • 直接通过函数URL测试:在函数的「代码+测试」页面获取触发URL,用Postman发送模拟Event Grid事件请求,验证函数是否响应。示例请求体:
[
  {
    "id": "12345",
    "eventType": "Microsoft.Storage.BlobCreated",
    "subject": "/blobServices/default/containers/primary/blobs/test.json",
    "eventTime": "2024-05-20T12:00:00Z",
    "data": {
      "Url": "https://yourstorageaccount.blob.core.windows.net/primary/test.json",
      "ContentType": "application/json"
    },
    "dataVersion": "1.0",
    "metadataVersion": "1",
    "topic": "/subscriptions/your-sub-id/resourceGroups/your-rg/providers/Microsoft.Storage/storageAccounts/yourstorageaccount"
  }
]

4. 正确创建Event Grid订阅

避免通过「测试主题」查找函数,直接从存储账户创建订阅:

  • 进入存储账户→「事件」→「+ 事件订阅」
  • 自定义订阅名称,选择事件类型为「BlobCreated」
  • 终点类型选「Azure 函数」,再选择目标函数应用与BlobTriggerFunction

代码优化建议

原代码逻辑可行,以下是可靠性优化点:

  1. 改用Uri解析Blob URL,避免字符串分割的不可靠性:
var blobUri = new Uri(blobCreatedData.Url);
var sourceContainerName = blobUri.Segments[2].TrimEnd('/');
var sourceBlobName = string.Join("", blobUri.Segments.Skip(3));
  1. 添加异常捕获,确保错误可追踪:
try
{
    // 原有业务逻辑
}
catch (Exception ex)
{
    _logger.LogError(ex, "处理Event Grid事件时发生错误");
    throw;
}

优化后完整代码

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Azure.Storage.Blobs;
using System.Text.Json;
using CsvHelper;
using System.Globalization;
using Azure.Messaging.EventGrid;
using System;

public class BlobTriggerFunction
{
    private readonly ILogger _logger;
    private readonly BlobServiceClient _blobServiceClient;

    public BlobTriggerFunction(ILoggerFactory loggerFactory, BlobServiceClient blobServiceClient)
    {
        _logger = loggerFactory.CreateLogger<BlobTriggerFunction>();
        _blobServiceClient = blobServiceClient;
    }

    [Function("BlobTriggerFunction")]
    public async Task Run([EventGridTrigger] EventGridEvent eventGridEvent)
    {
        _logger.LogInformation($"Event Grid trigger function processed an event: {eventGridEvent.EventType}");

        if (eventGridEvent.EventType != "Microsoft.Storage.BlobCreated")
        {
            _logger.LogInformation($"Ignoring event type: {eventGridEvent.EventType}");
            return;
        }

        try
        {
            var blobCreatedData = eventGridEvent.Data.ToObjectFromJson<BlobCreatedEventData>();
            var blobUri = new Uri(blobCreatedData.Url);
            var sourceContainerName = blobUri.Segments[2].TrimEnd('/');
            var sourceBlobName = string.Join("", blobUri.Segments.Skip(3));

            var destinationContainerName = "secondary";
            var destinationBlobName = Path.ChangeExtension(sourceBlobName, ".csv");

            var sourceBlobClient = _blobServiceClient.GetBlobContainerClient(sourceContainerName).GetBlobClient(sourceBlobName);
            var destinationBlobClient = _blobServiceClient.GetBlobContainerClient(destinationContainerName).GetBlobClient(destinationBlobName);

            // Download the source blob content
            using var sourceStream = await sourceBlobClient.OpenReadAsync();
            using var streamReader = new StreamReader(sourceStream);
            var jsonContent = await streamReader.ReadToEndAsync();

            // Parse JSON and convert to CSV
            var jsonArray = JsonSerializer.Deserialize<List<Dictionary<string, JsonElement>>>(jsonContent);
            using var memoryStream = new MemoryStream();
            using var writer = new StreamWriter(memoryStream);
            using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);

            if (jsonArray?.Any() == true)
            {
                var headers = jsonArray[0].Keys.ToArray();
                foreach (var header in headers)
                {
                    csv.WriteField(header);
                }
                csv.NextRecord();

                foreach (var item in jsonArray)
                {
                    foreach (var header in headers)
                    {
                        if (item.TryGetValue(header, out JsonElement value))
                        {
                            csv.WriteField(value.ToString());
                        }
                        else
                        {
                            csv.WriteField(string.Empty);
                        }
                    }
                    csv.NextRecord();
                }
            }

            writer.Flush();
            memoryStream.Position = 0;

            // Upload the CSV to the destination container
            await destinationBlobClient.UploadAsync(memoryStream, true);

            _logger.LogInformation($"CSV file created and uploaded: {destinationBlobName}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "处理BlobCreated事件时发生未预期错误");
            throw;
        }
    }
}

public class BlobCreatedEventData
{
    public string Api { get; set; }
    public string ClientRequestId { get; set; }
    public string RequestId { get; set; }
    public string ETag { get; set; }
    public string ContentType { get; set; }
    public int ContentLength { get; set; }
    public string BlobType { get; set; }
    public string Url { get; set; }
    public string Sequencer { get; set; }
    public StorageDiagnostics StorageDiagnostics { get; set; }
}

public class StorageDiagnostics
{
    public string BatchId { get; set; }
}

内容的提问来源于stack exchange,提问作者Bridgette Ryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:05:16