Azure表存储返回400错误请求。

139
我在调试模式下运行了这个程序,并附上了异常细节的图片。我该如何知道出了什么问题?我试图向表中插入数据。Azure不能给我更多的细节吗?
注:存储在Windows Azure上,而不是我的机器上。表已创建,但在插入数据时出现错误。

enter image description here

// Retrieve the storage account from the connection string.
Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=***;AccountKey=***");

// Create the table client.
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

// Create the table if it doesn't exist.
CloudTable table = tableClient.GetTableReference("EmployeeOnlineHistory");
table.CreateIfNotExists();

这里是插入代码:

public static void SetStatus(Employee e, bool value)
{
    try
    {
        // Retrieve the storage account from the connection string.
        Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=###;AccountKey=###");

        // Create the table client.
        CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

        // Create the CloudTable object that represents the "people" table.
        CloudTable table = tableClient.GetTableReference("EmployeeOnlineHistory");

        // Create a new customer entity.

        if (value == true)
        {
            EmployeeOnlineHistory empHistory = new EmployeeOnlineHistory(e.Id);
            empHistory.IsOnline = true;
            empHistory.OnlineTimestamp = DateTime.Now;
            TableOperation insertOperation = TableOperation.Insert(empHistory);
            table.Execute(insertOperation);
        }
        else
        {
            TableQuery<EmployeeOnlineHistory> query = new TableQuery<EmployeeOnlineHistory>()
                .Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, e.Id.ToString()));
            EmployeeOnlineHistory entity = table.ExecuteQuery(query).Take(1).FirstOrDefault();

            if ((entity!=null)&&(entity.IsOnline))
            {
                entity.IsOnline = false;
                entity.OfflineTimestamp = DateTime.Now;
                entity.OnlineTime = (entity.OfflineTimestamp - entity.OnlineTimestamp);
                TableOperation updateOperation = TableOperation.Replace(entity);
                table.Execute(updateOperation);
            }
            else
            {
                EmployeeOnlineHistory empHistory = new EmployeeOnlineHistory(e.Id);
                empHistory.IsOnline = false;
                empHistory.OfflineTimestamp = DateTime.Now;
                TableOperation insertOperation = TableOperation.Insert(empHistory);
                table.Execute(insertOperation);
            }
        }
    }
    catch (Exception ex)
    {
        //var details = new System.IO.StreamReader(((Microsoft.WindowsAzure.Storage.StorageException)ex)..Response.GetResponseStream()).ReadToEnd();
        LogFile.Error("EmployeeOnlineHistory.setStatus",ex);
    }
}

那张图片并没有太大的帮助,除了确认错误400之外。如果展示你执行的代码,包括如何设置存储客户端、如何设置表格,然后进行插入等操作,会更有帮助。 - David Makogon
我复制粘贴了代码。希望这有所帮助。 - Ryan
1
如果您能说明是哪个情况出了问题,那将会很有帮助。此外,您显然在代码之外设置了一些属性(例如PartitionKey和RowKey),因此了解这些属性以及它们被设置为什么将会很有帮助。 - Brian Reischl
我注意到当异常首先被抛出时,没有“查看详细信息”选项,但是当您继续调试并且流程返回到调用者(异常再次弹出的地方)时,您可以看到“查看详细信息”选项。从那里,您可以使用@Juha Palomäki的答案找到错误。 - S Raghav
22个回答

181

400错误意味着您的某个属性的值有问题。一种找出问题的方法是通过Fiddler跟踪请求/响应,查看实际发送到Windows Azure Storage的数据。

根据快速浏览您的代码的猜测,在您的模型中可能有一些日期/时间类型属性(OfflineTimestamp、OnlineTimestamp),并且在某些情况下,其中一个属性被初始化为默认值"DateTime.MinValue"。请注意,Windows Azure中日期/时间类型属性允许的最小值为1601年1月1日(UTC)[http://msdn.microsoft.com/en-us/library/windowsazure/dd179338.aspx]。请检查是否存在这种情况。如果是这样的话,您可以将它们变成可空类型字段,以免填充默认值。

同时,也可以查看Juha Palomäki的下面的答案……异常中有时候会有稍微更有用的消息,他建议检查(RequestInformation.ExtendedErrorInformation.ErrorMessage)。


297
求求你了,如果Azure团队的某个人看到这个信息,请让SDK返回比400错误更多的信息。我不知道为什么存储表中的DateTime不能有与.NET DateTime对象相同的最小日期,但我浪费了一整天的时间来解决这个问题。在我找出引起问题的属性之前,我还发现了另一个问题,这对我有所帮助。现在,在将DateTime插入/更新到存储表中的模型之前,我必须对所有DateTime属性运行检查。这样做并不理想... - Michael
8
顺便提一下,你也不能拥有一个枚举类型的属性。我不得不将该属性转换为整数。 - Martin
20
显然,表名也不能有连字符。我花了一个小时才弄明白这一点。 - Amogh Natu
5
表名也不能有下划线..这就是我在这里的原因。 - Quango
2
我想RowKey没有用空字符串初始化,因为这是主要查找值和唯一索引列...它的目的是提醒你填充它,我认为应该是这样....虽然这只是我的猜测...至于表名...请阅读这个...https://blogs.msdn.microsoft.com/jmstall/2014/06/12/azure-storage-naming-rules/ - dreadeddev
显示剩余11条评论

152

StorageException包含更详细的错误信息。

在调试器中检查:StorageException.RequestInformation.ExtendedInformation

enter image description here


10
为什么这个信息不是最高级别的异常? - Wilko van der Veen
对我来说,这突出显示了..“附加blob不受仿真器支持”..FML - Michiel Cornille
指定的资源名称包含无效字符。我的表名中有破折号,就像我的队列名称一样... 希望搜索能够让更多人发现!请参见:https://dev59.com/M6Pia4cB1Zd3GeqP3L5T - Nateous
我在表名方面遇到了类似的问题。在表存储中,破折号是不允许的,但在 Cosmos DB 中是可以的。 - bytedev

62
在我的情况下,RowKey 中的斜杠导致了问题。当我尝试通过存储模拟器手动添加时,我还收到了“OutOfRangeInput - One of the request inputs is out of range.” 的错误提示。
以下是 PartitionKeyRowKey 属性值中不允许使用的字符:
- 斜杠(/)字符 - 反斜杠(\)字符 - 数字符号(#)字符 - 问号(?)字符 - 控制字符从 U+0000 到 U+001F,包括: - 水平制表符(\t)字符 - 换行符(\n)字符 - 回车符(\r)字符 - 控制字符从 U+007F 到 U+009F
更多信息请参考http://msdn.microsoft.com/en-us/library/dd179338.aspx。我编写了一个扩展方法来处理这个问题。
public static string ToAzureKeyString(this string str)
{
    var sb = new StringBuilder();
    foreach (var c in str
        .Where(c => c != '/'
                    && c != '\\'
                    && c != '#'
                    && c != '/'
                    && c != '?'
                    && !char.IsControl(c)))
        sb.Append(c);
    return sb.ToString();
}

4
这也是我的问题。你的扩展方法完美解决了这个问题! - James Wilson
1
我喜欢扩展方法。救了我。 - Newton Sheikh
这里提供了另一种响应方式,它使用正则表达式替换:https://dev59.com/smIk5IYBdhLWcg3wFKlK#28788382 - ΩmegaMan
已经为我修复了。我有一个正斜杠。看起来像一个伪复合键的好字符。 - richard
1
这个扩展方法没有删除一些不允许的字符,例如空格、"("、")"等。https://learn.microsoft.com/en-us/rest/api/storageservices/Understanding-the-Table-Service-Data-Model?redirectedfrom=MSDN - Tiago Andrade e Silva
我收到了一个“无效查询语法”消息。将RowKey值从 DateTime.UtcNow.ToString() 更改为 DateTime.UtcNow.ToString(dd-MM-yyyy) 解决了这个问题。 - Hari

7
我也遇到了同样的问题,但是在我的情况中是因为大小造成的。经过对附加异常属性(RequestInformation.ExtendedErrorInformation)进行深入挖掘,找到了原因:
错误代码:PropertyValueTooLarge 错误消息:属性值超过允许的最大大小 (64KB)。如果属性值是字符串,则应该使用 UTF-16 编码,并且最大字符数应该小于等于 32K。

5

好的,就我来说,我试图做到以下几点:

CloudBlobContainer container = blobClient.GetContainerReference("SessionMaterials");
await container.CreateIfNotExistsAsync();

由于容器名称 SessionMaterials(通常采用 Pascal Case 和 Camel Case 写法 :D)导致了 400 错误请求。所以,我只需要将其改为 sessionmaterials。 然后就可以正常工作了。
希望对某些人有所帮助。
PS:只需检查异常的 HTTP 响应或使用 Fiddler 捕获请求和响应即可。

3
有时候是因为你的 partitionKey 或者 rowKeyNULL (对我来说就是这种情况)。

3

在我的情况下: 容器名称是大写字母。使用字符时存在一些限制。 在此输入图片描述


这同样适用于表属性名称。 - Iain Ballard

1

您可以在此处找到有关所有表服务错误代码的微软文档。


1
我遇到了同样的 BadRequest(400) 错误,最后我手动填写了:

enter image description here

它对我起了作用,希望这可以帮到您!


这个答案是正确的。我浪费了大约一个小时,结果发现即使是Timestamp也必须手动生成。这真的很烦人。 - Roman Koliada

1
我遇到了 (400) 错误请求,状态消息为 Bad Request,错误代码为 OutOfRangeInput,当实体的 DateTime 属性未设置 (= DateTime.MinValue) 时。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接