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

ASP.NET Core 8 Web API中Audit.NET对接Azure表存储的PartitionKey错误

问题:Azure表存储PartitionKey超出范围错误(Audit.NET + ASP.NET Core 8)

在ASP.NET Core 8 Web API中基于Audit.NET实现审计追踪,使用AzureStorageTableDataProvider存储审计事件时,遇到PartitionKey相关的400错误:

The 'PartitionKey' parameter of value 'GET:https://demoapi.azurewebsites.net/api/product/23' is out of range with RequestId:adb38aea-c002-0061-436d-d408ff000000
Time:2024-07-12T15:08:43.7480899Z
Status: 400 (Bad Request)
ErrorCode: OutOfRangeInput
Content:
{ "odata.error":{"code":"OutOfRangeInput","message":{"lang":"en-US","value":"The 'PartitionKey' parameter of value 'GET:https://demoapi.azurewebsites.net/api/product/23' is out of range.\nRequestId:adb38aea-c002-0061-436d-d408ff000000\nTime:2024-07-12T15:08:43.7480899Z"}}}

原配置代码如下:

public void AuditSetupOutput(IApplicationBuilder app)
{
    var options = new JsonSerializerOptions { WriteIndented = true };

    Configuration.Setup()
        .JsonSystemAdapter(options)
        .UseAzureTableStorage(config => config
            .Endpoint(new Uri("https://account.table.core.windows.net"), new DefaultAzureCredential())
            .TableName(evt => $"{_TargetTableName}{DateTime.UtcNow:MMMyyyy}")
            .ClientOptions(new TableClientOptions { Retry = { MaxRetries = 3 } })
            .EntityBuilder(builder => builder
                .PartitionKey(auditEvent => auditEvent.EventType)
                .RowKey(auditEvent => Guid.NewGuid().ToString("N"))
                .Columns(col => col
                    .FromDictionary(auditEvent => new Dictionary<string, object>
                    {
                        { "EventType", auditEvent.EventType },
                        { "UserName", auditEvent.Environment.UserName },
                        { "EventDuration", auditEvent.Duration },
                        { "DataSize", auditEvent.ToJson().Length },
                        { "Data", auditEvent.ToJson().Length >= 32000 ? CompressAuditEventData(auditEvent.ToJson()) : auditEvent.ToJson() }
                    })));

    var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
    {
        scope.SetCustomField("TraceId", httpContextAccessor.HttpContext?.TraceIdentifier);
    });
}

原因分析

Azure表存储对PartitionKey有严格限制:

  • 最大长度为1024字节(UTF-8编码)
  • 不能包含斜杠(/)、反斜杠(\)、#、?等特殊字符
  • 不能以空格结尾

Audit.NET的WebApi审计事件默认EventType为HTTP方法:完整请求URL(如GET:https://demoapi.azurewebsites.net/api/product/23),该值包含禁用的斜杠字符,且如果URL过长会直接超出1024字节限制,触发OutOfRangeInput错误。


解决方法

需要修改PartitionKey的生成逻辑,满足Azure表存储的限制:

  1. 缩短长度:使用更简洁的标识(如HTTP方法+控制器/动作名称)
  2. 哈希处理:对原EventType进行哈希运算,生成固定长度的合法字符串
  3. 清理特殊字符:移除或替换PartitionKey中的禁用字符

参考实现代码

方案1:使用HTTP方法+控制器/动作名称

从AuditWebApi事件中提取控制器和动作名称,生成简洁合规的PartitionKey:

public void AuditSetupOutput(IApplicationBuilder app)
{
    var options = new JsonSerializerOptions { WriteIndented = true };

    Configuration.Setup()
        .JsonSystemAdapter(options)
        .UseAzureTableStorage(config => config
            .Endpoint(new Uri("https://account.table.core.windows.net"), new DefaultAzureCredential())
            .TableName(evt => $"{_TargetTableName}{DateTime.UtcNow:MMMyyyy}")
            .ClientOptions(new TableClientOptions { Retry = { MaxRetries = 3 } })
            .EntityBuilder(builder => builder
                // 修改PartitionKey生成逻辑:提取HTTP方法+控制器/动作名称
                .PartitionKey(auditEvent => 
                {
                    var webApiAction = auditEvent.GetWebApiAuditAction();
                    return $"{webApiAction.HttpMethod}:{webApiAction.ControllerName}/{webApiAction.ActionName}";
                })
                .RowKey(auditEvent => Guid.NewGuid().ToString("N"))
                .Columns(col => col
                    .FromDictionary(auditEvent => new Dictionary<string, object>
                    {
                        { "EventType", auditEvent.EventType },
                        { "UserName", auditEvent.Environment.UserName },
                        { "EventDuration", auditEvent.Duration },
                        { "DataSize", auditEvent.ToJson().Length },
                        { "Data", auditEvent.ToJson().Length >= 32000 ? CompressAuditEventData(auditEvent.ToJson()) : auditEvent.ToJson() }
                    })));

    var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
    {
        scope.SetCustomField("TraceId", httpContextAccessor.HttpContext?.TraceIdentifier);
    });
}

方案2:哈希处理EventType

对原EventType进行SHA256哈希,生成固定长度且无特殊字符的PartitionKey:

using System.Security.Cryptography;
using System.Text;

public void AuditSetupOutput(IApplicationBuilder app)
{
    var options = new JsonSerializerOptions { WriteIndented = true };

    Configuration.Setup()
        .JsonSystemAdapter(options)
        .UseAzureTableStorage(config => config
            .Endpoint(new Uri("https://account.table.core.windows.net"), new DefaultAzureCredential())
            .TableName(evt => $"{_TargetTableName}{DateTime.UtcNow:MMMyyyy}")
            .ClientOptions(new TableClientOptions { Retry = { MaxRetries = 3 } })
            .EntityBuilder(builder => builder
                // 修改PartitionKey为EventType的哈希值,替换Azure禁用的字符
                .PartitionKey(auditEvent => 
                {
                    using var sha256 = SHA256.Create();
                    var eventTypeBytes = Encoding.UTF8.GetBytes(auditEvent.EventType);
                    var hashBytes = sha256.ComputeHash(eventTypeBytes);
                    // 转换为Base64并替换Azure禁用的字符
                    return Convert.ToBase64String(hashBytes)
                        .Replace("/", "_")
                        .Replace("+", "-")
                        .TrimEnd('=');
                })
                .RowKey(auditEvent => Guid.NewGuid().ToString("N"))
                .Columns(col => col
                    .FromDictionary(auditEvent => new Dictionary<string, object>
                    {
                        { "EventType", auditEvent.EventType },
                        { "UserName", auditEvent.Environment.UserName },
                        { "EventDuration", auditEvent.Duration },
                        { "DataSize", auditEvent.ToJson().Length },
                        { "Data", auditEvent.ToJson().Length >= 32000 ? CompressAuditEventData(auditEvent.ToJson()) : auditEvent.ToJson() }
                    })));

    var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
    {
        scope.SetCustomField("TraceId", httpContextAccessor.HttpContext?.TraceIdentifier);
    });
}

内容的提问来源于stack exchange,提问作者santosh kumar patro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:32:04