Azure Table Storage devuelve 400 solicitudes incorrectas

119

Ejecuté esto en modo de depuración y adjunto una imagen con los detalles de la excepción. ¿Cómo puedo saber qué salió mal? Estaba intentando insertar datos en una tabla. ¿No puede azure darme más detalles?

Obs: el almacenamiento está en Windows Azure, no en mi máquina. Se crearon las tablas, pero obtengo este error al insertar datos

ingrese la descripción de la imagen aquí

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

y aquí está el código de inserción:

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);
    }
}
Ryan
fuente
Esa imagen realmente no ayuda, aparte de confirmar el error 400. "Ayudaría" es mostrar el código que ejecutó que resultó en la solicitud incorrecta: cómo configuró el cliente de almacenamiento, cómo configuró la tabla, luego para insertar, etc.
David Makogon
Copié y pegué el código. Espero que esto ayude
Ryan
Sería útil si dijera cuál de los varios casos está fallando. Además, aparentemente está configurando algunas propiedades fuera de este código (por ejemplo, PartitionKey y RowKey), por lo que sería útil saber cuáles son y para qué se configuran.
Brian Reischl
Me di cuenta de que cuando se lanza la excepción primero, no hay la opción "Ver detalles", pero cuando continúa depurando y el flujo vuelve a la persona que llama (donde aparece la excepción nuevamente), puede ver la opción Ver detalles. Desde allí, puede usar la respuesta de @Juha Palomäki para encontrar el error
raghav710

Respuestas:

148

400 Error significa que hay algún problema con el valor de una de sus propiedades. Una forma de averiguarlo es rastrear la solicitud / respuesta a través de Fiddler y ver los datos reales que se envían a Windows Azure Storage.

Tomando una suposición salvaje, supongo que, al echar un vistazo rápido a su código, en su modelo tiene algunas propiedades de tipo Fecha / Hora (OfflineTimestamp, OnlineTimestamp) y observé que en ciertos escenarios uno de ellos se inicializa con el valor predeterminado que es " DateTime.MinValue ". Tenga en cuenta que el valor mínimo permitido para un atributo de tipo de fecha / hora es el 1 de enero de 1601 (UTC) en Windows Azure [http://msdn.microsoft.com/en-us/library/windowsazure/dd179338.aspx] . Por favor, vea si ese no es el caso. Si ese es el caso, puede convertirlos en campos de tipo anulable para que no se llenen con los valores predeterminados.

Eche un vistazo a la respuesta de Juha Palomäki a continuación también ... a veces hay un mensaje un poco más útil en la excepción donde sugiere (RequestInformation.ExtendedErrorInformation.ErrorMessage)

Dejó de contribuir
fuente
259
Por el amor de Dios, si alguien del equipo de Azure lee esto, haga que el SDK devuelva más información que el error 400 Bad Request. No tengo idea de por qué el DateTime en el almacenamiento de la tabla no puede tener la misma fecha mínima que el objeto .NET DateTime, pero desperdicié un buen día en este. Cuando llegué a ver qué propiedad causó el problema, me encontré con esto también, lo que ayudó. Ahora, antes de insertar / actualizar un modelo con un DateTime para el almacenamiento de la tabla, tengo que ejecutar comprobaciones en todas las propiedades de DateTime. NO es ideal ...
Michael
8
Mientras estamos en eso, tampoco puede tener una propiedad enum. Tuve que convertir la propiedad a Integer
Martin
15
Aparentemente, los nombres de las tablas tampoco pueden tener guiones. Me tomó una hora darme cuenta de eso.
Amogh Natu
4
Los nombres de las tablas tampoco pueden tener guiones bajos ... por eso estoy aquí
Quango
2
Me imagino que RowKey no se inicializa con una cadena vacía, ya que este es el valor de búsqueda principal y solo la columna indexada ... es para recordarle que debe completarla, habría pensado ... esto es solo mi suposición ... los nombres de las tablas van ... lea esto ... blogs.msdn.microsoft.com/jmstall/2014/06/12/…
dreadeddev
129

StorageException también contiene información un poco más detallada sobre el error.

Comprobar en el depurador: StorageException.RequestInformation.ExtendedInformation

ingrese la descripción de la imagen aquí

Juha Palomäki
fuente
6
¿Por qué esta información no es una excepción de nivel superior?
Wilko van der Veen
Para mí esto resaltado .. "Agregar blob no es compatible con el emulador" .. FML
Michiel Cornille
The specifed resource name contains invalid characters.el nombre de mi mesa tenía guiones ... al igual que los nombres de mi cola ... suspiro. ¡Esperamos que la búsqueda recoja esto para más personas! ver: stackoverflow.com/questions/45305556/…
Nateous
55

En mi caso, fue una barra diagonal en RowKey .

También recibí un 'OutOfRangeInput: una de las entradas de solicitud está fuera de rango'. Error al intentar agregar manualmente a través del emulador de almacenamiento.

Caracteres no permitidos en campos clave

Los siguientes caracteres no están permitidos en los valores de las propiedades PartitionKey y RowKey :

  • El carácter de barra diagonal ( / )
  • El carácter de barra invertida ( \ )
  • El carácter de signo de número ( # )
  • El carácter de signo de interrogación ( ? )
  • Caracteres de control de U + 0000 a U + 001F , incluyendo:
    • El carácter de tabulación horizontal ( \ t )
    • El carácter de salto de línea ( \ n )
    • El carácter de retorno de carro ( \ r )
    • Caracteres de control de U + 007F a U + 009F

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

Escribí un método de extensión para manejar esto por mí.

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();
}
Shawn McGough
fuente
4
Este también era mi problema. ¡Tu método de extensión funciona como un campeón!
James Wilson
1
Me encantó el método de extensión. Me salvó.
Newton Sheikh
Respuesta alternativa dada aquí que reemplaza una expresión regular stackoverflow.com/a/28788382/285795
ΩmegaMan
Me lo arregló. Tenía una barra inclinada hacia adelante. Parecía un buen carácter para una clave pseudocompuesta.
Richard
Este método de extensión no elimina varios caracteres que no están permitidos. Por ejemplo: espacio, "(", ")" ... docs.microsoft.com/en-us/rest/api/storageservices/…
Tiago Andrade e Silva
6

Enfrenté el mismo problema, pero la razón en mi caso se debió al tamaño. Después de profundizar en las propiedades de excepción adicionales (RequestInformation.ExtendedErrorInformation), encontré el motivo:

ErrorCode: PropertyValueTooLarge ErrorMessage: El valor de la propiedad excede el tamaño máximo permitido (64 KB). Si el valor de la propiedad es una cadena, está codificado en UTF-16 y el número máximo de caracteres debe ser 32 K o menos.

Nibras Manna
fuente
5

bueno, en mi caso estaba tratando de hacer esto:

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

debido a ContainerName SessionMaterials(como un hábito de escribir en Pascal Case y Camel Case: D) estaba causando 400 solicitudes incorrectas. Entonces, solo tengo que hacerlo sessionmaterials. Y funcionó.

Espero que esto ayude a alguien.

PD: - Simplemente verifique la respuesta http de excepción o use Fiddler para capturar la solicitud y la respuesta.

Jawand Singh
fuente
3

en mi caso: El nombre del contenedor estaba en mayúscula. existen limitaciones al usar caracteres. ingrese la descripción de la imagen aquí

Ravi Anand
fuente
Esto también se aplica a los nombres de propiedades de la tabla.
Iain Ballard
3

A veces es porque tu partitionKeyo rowKeyesNULL

(fue el caso para mí)

Maroine Abdellah
fuente
1

Puede encontrar una documentación de MS sobre todos los códigos de error del servicio de mesa aquí

huha
fuente
1

Tuve el mismo error BadRequest (400), al final lo llené manualmente:

ingrese la descripción de la imagen aquí

Y funcionó para mí. ¡Espero que esto ayude!

Gatsby
fuente
Esta respuesta es correcta. Perdí alrededor de una hora y se desconecta que incluso se Timestampdebe generar manualmente. Es muy molesto.
Roman Koliada
0

También enfrenté el mismo tipo de problema. En mi caso, el valor de PartitionKey no se estableció, por lo que, por defecto, el valor de PartitionKey era nulo, lo que resultó en una Object reference not set to an instance of an object.excepción

Verifique si está proporcionando los valores apropiados para PartitionKey o RowKey, puede enfrentar tal problema.

Dilip Nannaware
fuente
0

Arreglé mis casos y funcionó bien

Mis casos:

  1. La clave de fila no tiene el formato correcto (400).
  2. La combinación de clave de partición y clave de fila no es única (409).
Kurkula
fuente
0

Recibí una solicitud incorrecta de 400 porque estaba usando ZRS (Zone Redundant Storage) y Analytics no está disponible para este tipo de almacenamiento. No sabía que estaba usando Analytics.

Eliminé el contenedor de almacenamiento y lo recreé como GRS y ahora funciona bien.

Aidan
fuente
0

Recibía una solicitud incorrecta (400), StatusMessage: Solicitud incorrecta, Código de error: OutOfRangeInput cuando la entidad tenía una propiedad DateTime no establecida (= DateTime.MinValue)

Stig
fuente
0

En mi caso: incluí metadatos de blob con un nombre de etiqueta que contenía un guión.

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

El guión "added-by"fue el problema, y ​​luego RTFM me dijo que los nombres de las etiquetas deben ajustarse a las convenciones de identificadores de C #.

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

El subrayado funciona bien.

Steve Friedl
fuente
0

En mi caso, no debería agregar PartitionKey y Rowkey en mi clase de entidad. Debería ser de la clase base. A continuación simplemente funcionaría.

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


      public TableRunLogMessage() { }
}
alegre
fuente
0

Si está utilizando NodeJS y tropezó con esta publicación, solo para descubrir que no obtiene esa hermosa información detallada en su objeto de error; puede utilizar un proxy para obtener esos detalles. Sin embargo, dado que nadie aquí menciona CÓMO usar un proxy.

La forma más sencilla con NodeJS es establecer dos variables ambientales:

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.

Si realmente está utilizando C #, como lo está haciendo el autor de esta publicación; simplemente puede instalar Fiddler y configurarlo para interceptar. Por defecto, debería interceptar las solicitudes. Es posible que también deba confiar en el certificado de Fiddler o hacer el equivalente de "NODE_TLS_REJECT_UNAUTHORIZED = 0" del nodo.

Doug
fuente
0

Obtuve una respuesta 400-BadRequest de la API de tabla de cuentas de almacenamiento de Azure. La información de excepción mostró que "La cuenta a la que se accede no es compatible con http". Supuse que debemos usar https en la cadena de conexión cuando "Se requiere transferencia segura" está habilitado en la configuración de la cuenta de almacenamiento, como se muestra en la siguiente imagen.ingrese la descripción de la imagen aquí

Kalaiselvan
fuente
0

En mi caso, para crear una nueva instalación de la clase "TableBotDataStore" (marco de bots de MS), pasamos el parámetro "tableName" con un guión como "master-bot" y TableBotDataStore puede tener nombres de tabla solo con letras y números.

igor_bugaenko
fuente
0

Tuve el mismo problema, la función pasaba containerNameKeycomo cadena. a continuación se muestra el código que dio error

container = blobClient.GetContainerReference(containerNameKey) 

Lo cambié a

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

Funcionó

Vani
fuente