O armazenamento de tabelas do Azure retorna 400 Bad Request


119

Eu executei isso no modo de depuração e anexei uma imagem com os detalhes da exceção. Como posso saber o que deu errado? Eu estava tentando inserir dados em uma tabela. O azure não pode me dar mais detalhes?

Obs: O armazenamento está no Windows Azure e não na minha máquina. As tabelas foram criadas, mas recebo este erro ao inserir dados

insira a descrição da imagem aqui

// 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();

e aqui está o código de inserção:

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

Essa imagem não ajuda muito, além de confirmar o erro 400. Será que "iria" ajudar é mostrar o código que você executou que resultou na solicitação incorreta: como você configurou o cliente de armazenamento, como configurou a mesa e depois foi inserir, etc.
David Makogon

Copiei e colei o código. Espero que isso ajude
Ryan

Ajudaria se você dissesse qual dos vários casos está falhando. Além disso, aparentemente você está definindo algumas propriedades fora deste código (por exemplo, PartitionKey e RowKey), portanto, seria útil saber quais são e para que estão sendo definidas.
Brian Reischl

Percebi que quando a exceção é lançada primeiro, não há opção "Exibir detalhes", mas quando você continua a depuração e o fluxo volta para o chamador (onde a exceção aparece novamente), você pode ver a opção Exibir detalhes. A partir daí, você pode usar a resposta de @Juha Palomäki para encontrar o erro
raghav710

Respostas:


148

Erro 400 significa que há algo errado com o valor de uma de suas propriedades. Uma maneira de descobrir é rastrear a solicitação / resposta por meio do Fiddler e ver os dados reais sendo enviados ao Armazenamento do Windows Azure.

Fazendo um palpite, estou assumindo, dando uma rápida olhada em seu código, que em seu modelo você tem algumas propriedades do tipo Date / Time (OfflineTimestamp, OnlineTimestamp) e observei que em certos cenários um deles é inicializado com o valor padrão que é " DateTime.MinValue ". Observe que o valor mínimo permitido para um atributo do tipo Data / Hora é 1º de janeiro de 1601 (UTC) no Windows Azure [http://msdn.microsoft.com/en-us/library/windowsazure/dd179338.aspx] . Por favor, veja se não é esse o caso. Se for esse o caso, você pode torná-los campos do tipo anuláveis ​​para que não sejam preenchidos com os valores padrão.

Dê uma olhada na resposta de Juha Palomäki abaixo também ... às vezes há uma mensagem um pouco mais útil na exceção onde ele sugere (RequestInformation.ExtendedErrorInformation.ErrorMessage)


259
Pelo amor de Deus, se alguém da equipe do Azure ler isso, faça o SDK retornar mais informações do que o erro 400 Bad Request. Não tenho ideia de por que DateTime no armazenamento de tabela não pode ter a mesma data mínima que o objeto .NET DateTime, mas perdi um bom dia com este. Quando descobri qual propriedade estava causando o problema, também me deparei com isso, o que ajudou. Agora, antes de inserir / atualizar um modelo com um DateTime para armazenamento de tabela, preciso executar verificações em todas as propriedades de DateTime. NÃO ideal ...
Michael,

8
Já que estamos nisso, você não pode ter uma propriedade enum também. Tive que converter a propriedade em Inteiro
Martin

15
Aparentemente, os nomes das tabelas também não podem ter hífens. Levei uma hora para descobrir isso.
Amogh Natu

4
Os nomes das tabelas também não podem ter sublinhados .. é por isso que estou aqui
Quango

2
Eu imagino que RowKey não foi inicializado com uma string vazia, pois este é o valor de pesquisa principal e apenas a coluna indexada ... é para lembrá-lo de preenchê-la, eu pensaria ... isso é apenas meu palpite ... No que diz respeito a nomes de tabelas vá .. leia isso ... blogs.msdn.microsoft.com/jmstall/2014/06/12/…
dreadeddev

129

A StorageException também contém informações um pouco mais detalhadas sobre o erro.

Verifique no depurador: StorageException.RequestInformation.ExtendedInformation

insira a descrição da imagem aqui


6
Por que essas informações não são uma exceção de nível superior?
Wilko van der Veen

Para mim, está destacado .. "Append blob não suportado pelo emulador" .. FML
Michiel Cornille

The specifed resource name contains invalid characters.o nome da minha mesa tinha travessões ... assim como os nomes das minhas filas ... suspiro. Esperançosamente, a pesquisa ajudará mais pessoas! consulte: stackoverflow.com/questions/45305556/…
Nateous

55

No meu caso, foi uma barra no RowKey .

Também recebi um 'OutOfRangeInput - uma das entradas de solicitação está fora do intervalo.' erro ao tentar adicionar manualmente por meio do emulador de armazenamento.

Caracteres não permitidos em campos-chave

Os seguintes caracteres não são permitidos em valores para as propriedades PartitionKey e RowKey :

  • O caractere de barra ( / )
  • O caractere de barra invertida ( \ )
  • O caractere de cardinal ( # )
  • O caractere de ponto de interrogação ( ? )
  • Caracteres de controle de U + 0000 a U + 001F , incluindo:
    • O caractere de tabulação horizontal ( \ t )
    • O caractere de avanço de linha ( \ n )
    • O caractere de retorno de carro ( \ r )
    • Controlar caracteres de U + 007F a U + 009F

http://msdn.microsoft.com/en-us/library/dd179338.aspx

Eu escrevi um método de extensão para lidar com isso para mim.

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
Este foi o meu problema também. Seu método de extensão funciona como um campeão!
James Wilson

1
Adorei o Método de Extensão. Me salvou.
Newton Sheikh

Resposta alternativa fornecida aqui que faz uma regex substituir stackoverflow.com/a/28788382/285795
ΩmegaMan

Consertou para mim. Eu tive uma barra para a frente. Parecia um bom caractere para uma chave pseudo composta.
richard,

Este método de extensão não remove vários caracteres que não são permitidos. Ex: espaço, "(", ")" ... docs.microsoft.com/en-us/rest/api/storageservices/…
Tiago Andrade e Silva

6

Enfrentei o mesmo problema, mas o motivo no meu caso foi devido ao tamanho. Depois de pesquisar as propriedades de exceção adicionais (RequestInformation.ExtendedErrorInformation), encontrou o motivo:

ErrorCode: PropertyValueTooLarge ErrorMessage: O valor da propriedade excede o tamanho máximo permitido (64 KB). Se o valor da propriedade for uma string, ele será codificado em UTF-16 e o ​​número máximo de caracteres deverá ser 32 K ou menos.


5

bem, no meu caso, eu estava tentando fazer isso:

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

por causa de ContainerName SessionMaterials(como um hábito de escrever em Pascal Case e Camel Case: D) estava causando 400 solicitações incorretas. Então, eu só tenho que fazer isso sessionmaterials. e funcionou.

Espero que isso ajude alguém.

PS: - Basta verificar a resposta http da exceção ou usar o fiddler para capturar a solicitação e a resposta.


3

no meu caso: o nome do contêiner estava em maiúscula. existem limitações ao usar chars. insira a descrição da imagem aqui


Isso também vale para nomes de propriedades de tabela.
Iain Ballard,


1

Uma documentação da MS sobre todos os códigos de erro do serviço de tabela pode ser encontrada aqui


1

Tive o mesmo erro BadRequest (400), no final preencho manualmente:

insira a descrição da imagem aqui

E funcionou para mim. Espero que isto ajude!


Esta resposta está correta. Perdi cerca de uma hora e acabou que até mesmo Timestampdeve ser gerado manualmente. É realmente irritante.
Roman Koliada

0

Eu também enfrentei o mesmo tipo de problema. No meu caso, o valor de PartitionKey não foi definido, portanto, por padrão, o valor de PartitionKey era nulo, o que resultou em Object reference not set to an instance of an object.exceção

Verifique se você está fornecendo os valores apropriados para PartitionKey ou RowKey, você pode enfrentar esse problema.


0

Eu consertei meus casos e funcionou bem

Meus casos:

  1. A chave de linha não está no formato correto (400).
  2. A combinação de chave de partição e chave de linha não é exclusiva (409).

0

Recebi um 400 Bad Request porque estava usando ZRS (Zone Redundant Storage) e o Analytics não está disponível para esse tipo de armazenamento. Eu não sabia que estava usando o Analytics.

Excluí o contêiner de armazenamento e recriei como GRS e agora funciona bem.


0

Eu estava recebendo um (400) Bad Request, StatusMessage: Bad Request, ErrorCode: OutOfRangeInput quando a entidade tinha uma propriedade DateTime não definida (= DateTime.MinValue)


0

No meu caso: incluí metadados de blob com um nome de tag contendo um hífen.

var blob = container.GetBlockBlobReference(filename);
blob.Metadata.Add("added-by", Environment.UserName);
//.. other metadata
blob.UploadFromStream(filestream);

O travessão "added-by"era o problema e, posteriormente, o RTFM me disse que os nomes das tags devem estar em conformidade com as convenções de identificador C #.

Ref: https://docs.microsoft.com/en-us/azure/storage/blobs/storage-properties-metadata

O sublinhado funciona bem.


0

No meu caso, não devo adicionar PartitionKey e Rowkey na minha classe de entidade. Deve ser da classe base. Abaixo apenas funcionaria.

public class TableRunLogMessage:TableEntity
{
      public string status { get; set; }
      public long logged { get; set; }


      public TableRunLogMessage() { }
}

0

Se você está usando o NodeJS e se deparou com esta postagem, apenas para descobrir que não obteve essas informações detalhadas adoráveis ​​em seu objeto de erro; você pode utilizar um proxy para obter esses detalhes. No entanto, como ninguém aqui menciona COMO usar um proxy.

A maneira mais simples com o NodeJS é definindo duas variáveis ​​ambientais:

NODE_TLS_REJECT_UNAUTHORIZED=0
This disables SSL checks so you can intercept your own SSL requests. This leaves you open to Man-in-The-Middle attacks and should NEVER make it to production, and I wouldn't even leave it in development for long. However, it will allow you to intercept the HTTP Requests.

HTTP_PROXY=http://127.0.0.1:8888
This sets node to utilize a proxy listening on your localhost at port 8888. Port 8888 is the default for Fiddler. Many other proxies default to 8080.

Se você estiver realmente utilizando C #, como o autor deste post está fazendo; você pode simplesmente instalar o Fiddler e configurá-lo para interceptar. Por padrão, ele deve interceptar as solicitações. Você também pode precisar confiar no certificado do Fiddler ou, de outra forma, fazer o equivalente ao "NODE_TLS_REJECT_UNAUTHORIZED = 0" do Node.


0

Recebi uma resposta 400-BadRequest da API da Tabela de Contas de Armazenamento do Azure. As informações de exceção mostraram que "A conta que está sendo acessada não suporta http.". Percebi que devemos usar https na string de conexão quando "Transferência segura necessária" estiver habilitada na configuração da conta de armazenamento, conforme mostrado na imagem abaixo.insira a descrição da imagem aqui


0

No meu caso, para criar uma nova instalação da classe "TableBotDataStore" (MS bot framework), passamos o parâmetro "tableName" com hífen como "master-bot" e TableBotDataStore pode ter nomes de tabelas apenas com letras e números


0

Tive o mesmo problema, a função estava passando o containerNameKeyas string. abaixo está o código que deu erro

container = blobClient.GetContainerReference(containerNameKey) 

Eu mudei para

container = blobClient.GetContainerReference(ConfigurationManager.AppSettings(containerNameKey).ToString()) 

Funcionou

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.