Примеры в этом посте были обновлены в сентябре для работы с текущей версией Windows Azure Storage REST API.
На форуме Windows Azure MSDN Azure иногда возникают вопросы об API-интерфейсе REST служб хранилища Windows Azure . Я иногда отвечал на них некоторыми примерами кода, показывающими, как использовать API. Я подумал, что было бы полезно привести некоторые примеры использования REST API для таблиц, больших двоичных объектов и очередей — если только так, мне не нужно углубляться в примеры, когда люди спрашивают, как его использовать. Этот пост не предназначен для предоставления полного описания REST API.
API REST полностью документирован (за исключением отсутствия рабочих примеров). Поскольку REST API — это окончательный способ обращения к Windows Azure Storage Services, я думаю, что люди, использующие API-интерфейс клиента хранилища более высокого уровня, должны иметь представление о REST API до уровня, позволяющего понять документацию. Понимание REST API может дать более глубокое понимание того, почему API-интерфейс Storage Storage ведет себя так, как он работает.
обманщик
Fiddler Web Debugging Proxy является важным инструментом при разработке с использованием REST (или клиента Storage) API , поскольку он фиксирует именно то , что передается по проводам к Windows Azure Storage Services.
авторизация
Почти каждый запрос к Windows Azure Storage Services должен пройти проверку подлинности. Исключением является доступ к BLOB-объектам с открытым доступом для чтения. Поддерживаемые схемы аутентификации для больших двоичных объектов, очередей и таблиц, и они описаны здесь . Запросы должны сопровождаться заголовком авторизации, созданным путем создания кода аутентификации сообщения на основе хеша с использованием хеша SHA-256 .
Ниже приведен пример выполнения хэша SHA-256 для заголовка авторизации:
public static String CreateAuthorizationHeader(String canonicalizedString) {
String signature = String.Empty;
using (HMACSHA256 hmacSha256 = new HMACSHA256( Convert.FromBase64String(storageAccountKey) )) {
Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
String authorizationHeader = String.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
AzureStorageConstants.SharedKeyAuthorizationScheme,
AzureStorageConstants.Account,
signature
);
return authorizationHeader;
}
Этот метод используется во всех примерах в этом посте.
AzureStorageConstants — это вспомогательный класс, содержащий различные константы. Ключ является секретным ключом для учетной записи Windows Azure Storage Services, указанной учетной записью . В приведенных здесь примерах SharedKeyAuthorizationScheme — это SharedKey .
Самым сложным в успешном использовании REST API является получение правильной строки для подписи. К счастью, в случае сбоя аутентификации служба Blob и служба очереди отвечают строкой авторизации, которую они использовали, и это можно сравнить со строкой авторизации, используемой при создании заголовка авторизации. Это значительно упростило использование API REST.
API Table Table
API Таблица Service поддерживает следующие операции на уровне таблиц:
API Таблица Service поддерживает следующие операции на уровне субъектов:
Эти операции реализованы с использованием соответствующего HTTP VERB:
- УДАЛИТЬ — удалить
- GET — запрос
- MERGE — объединить
- POST — вставить
- PUT — обновить
В этом разделе приведены примеры операций вставки сущностей и запросов.
Вставить объект
Метод InsertEntity (), указанный в этом разделе, вставляет в таблицу сущность с двумя свойствами String, Artist и Title . Сущность представляется в виде записи ATOM в теле запроса, отправленного в табличную службу. В этом примере запись ATOM генерируется методом GetRequestContentInsertXml (). Дата должна быть в формате RFC 1123 в заголовке x-ms-date, предоставленном канонизированному ресурсу, используемому для создания строки авторизации. Обратите внимание, что версия службы хранения установлена на «2012-02-12», что требует правильной установки DataServiceVersion и MaxDataServiceVersion .
public void InsertEntity(String tableName, String artist, String title)
{
String requestMethod = "POST";
String urlPath = tableName;
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String contentMD5 = String.Empty;
String contentType = "application/atom+xml";
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath);
String stringToSign = String.Format(
"{0}\n{1}\n{2}\n{3}\n{4}",
requestMethod,
contentMD5,
contentType,
dateInRfc1123Format,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
UTF8Encoding utf8Encoding = new UTF8Encoding();
Byte[] content = utf8Encoding.GetBytes(GetRequestContentInsertXml(artist, title));
Uri uri = new Uri(AzureStorageConstants.TableEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Accept = "application/atom+xml,application/xml";
request.ContentLength = content.Length;
request.ContentType = contentType;
request.Method = requestMethod;
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.Headers.Add("Accept-Charset", "UTF-8");
request.Headers.Add("DataServiceVersion", "2.0;NetFx");
request.Headers.Add("MaxDataServiceVersion", "2.0;NetFx");
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(content, 0, content.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
using (StreamReader reader = new StreamReader(dataStream))
{
String responseFromServer = reader.ReadToEnd();
}
}
}
private String GetRequestContentInsertXml(String artist, String title)
{
String defaultNameSpace = "http://www.w3.org/2005/Atom";
String dataservicesNameSpace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
String metadataNameSpace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.OmitXmlDeclaration = false;
xmlWriterSettings.Encoding = Encoding.UTF8;
StringBuilder entry = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(entry))
{
xmlWriter.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
xmlWriter.WriteWhitespace("\n");
xmlWriter.WriteStartElement("entry", defaultNameSpace);
xmlWriter.WriteAttributeString("xmlns", "d", null, dataservicesNameSpace);
xmlWriter.WriteAttributeString("xmlns", "m", null, metadataNameSpace);
xmlWriter.WriteElementString("title", null);
xmlWriter.WriteElementString("updated", String.Format("{0:o}", DateTime.UtcNow));
xmlWriter.WriteStartElement("author");
xmlWriter.WriteElementString("name", null);
xmlWriter.WriteEndElement();
xmlWriter.WriteElementString("id", null);
xmlWriter.WriteStartElement("content");
xmlWriter.WriteAttributeString("type", "application/xml");
xmlWriter.WriteStartElement("properties", metadataNameSpace);
xmlWriter.WriteElementString("PartitionKey", dataservicesNameSpace, artist);
xmlWriter.WriteElementString("RowKey", dataservicesNameSpace, title);
xmlWriter.WriteElementString("Artist", dataservicesNameSpace, artist);
xmlWriter.WriteElementString("Title", dataservicesNameSpace, title + "\n" + title);
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.Close();
}
String requestContent = entry.ToString();
return requestContent;
}
Это генерирует следующий запрос (как захвачено Fiddler):
POST https://STORAGE_ACCOUNT.table.core.windows.net/authors HTTP/1.1 Accept: application/atom+xml,application/xml Content-Type: application/atom+xml x-ms-date: Sun, 08 Sep 2013 06:31:12 GMT x-ms-version: 2012-02-12 Authorization: SharedKey STORAGE_ACCOUNT:w7Uu4wHZx4fFwa2bsxd/TJVZZ1AqMPwxvW+pYtoWHd0= Accept-Charset: UTF-8 DataServiceVersion: 2.0;NetFx MaxDataServiceVersion: 2.0;NetFx Host: STORAGE_ACCOUNT.table.core.windows.net Content-Length: 514 Expect: 100-continue Connection: Keep-Alive
Тело запроса:
<?xml version="1.0" encoding="UTF-8"?> <entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom"><title /><updated>2013-09-08T06:31:13.0503771Z</updated><author><name /></author><id /><content type="application/xml"><m:properties><d:PartitionKey>Beckett</d:PartitionKey><d:RowKey>Molloy</d:RowKey><d:Artist>Beckett</d:Artist> <d:Title>Molloy Molloy</d:Title></m:properties></content></entry>
Служба таблиц генерирует следующий ответ:
HTTP/1.1 201 Created Cache-Control: no-cache Content-Type: application/atom+xml;charset=utf-8 ETag: W/"datetime'2013-09-08T07%3A19%3A07.2189243Z'" Location: https://STORAGE_ACCOUNT.table.core.windows.net/authors(PartitionKey='Beckett',RowKey='Molloy') Server: Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: 3818433a-4d89-4344-bcf1-ec248cf24d97 x-ms-version: 2012-02-12 Date: Sun, 08 Sep 2013 07:19:07 GMT Content-Length: 1108
Служба таблиц генерирует следующее тело ответа:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xml:base="https://STORAGE_ACCOUNT.table.core.windows.net/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/"datetime'2013-09-08T07%3A19%3A07.2189243Z'"" xmlns="http://www.w3.org/2005/Atom">
<id>https://STORAGE_ACCOUNT.table.core.windows.net/authors(PartitionKey='Beckett',RowKey='Molloy')</id>
<title type="text"></title>
<updated>2013-09-08T07:19:07Z</updated>
<author>
<name />
</author>
<link rel="edit" title="authors" href="authors(PartitionKey='Beckett',RowKey='Molloy')" />
<category term="STORAGE_ACCOUNT.authors" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:PartitionKey>Beckett</d:PartitionKey>
<d:RowKey>Molloy</d:RowKey>
<d:Timestamp m:type="Edm.DateTime">2013-09-08T07:19:07.2189243Z</d:Timestamp>
<d:Artist>Beckett</d:Artist>
<d:Title>Molloy
Molloy</d:Title>
</m:properties>
</content>
</entry>
Обратите внимание, что я должен был URLEncoded PartitionKey и RowKey, но не сделал этого для простоты. На самом деле есть некоторые проблемы с кодировкой URL пробелов и других символов.
Получить сущность
Метод GetEntity (), описанный в этом разделе, извлекает один объект, вставленный в предыдущем разделе. Конкретная сущность, которую нужно получить, идентифицируется непосредственно в URL.
public void GetEntity(String tableName, String partitionKey, String rowKey)
{
String requestMethod = "GET";
String urlPath = String.Format("{0}(PartitionKey='{1}',RowKey='{2}')", tableName, partitionKey, rowKey);
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath);
String stringToSign = String.Format(
"{0}\n\n\n{1}\n{2}",
requestMethod,
dateInRfc1123Format,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(AzureStorageConstants.TableEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.Headers.Add("Accept-Charset", "UTF-8");
request.Accept = "application/atom+xml,application/xml";
request.Headers.Add("DataServiceVersion", "2.0;NetFx");
request.Headers.Add("MaxDataServiceVersion", "2.0;NetFx");
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
using (StreamReader reader = new StreamReader(dataStream))
{
String responseFromServer = reader.ReadToEnd();
}
}
}
Это генерирует следующий запрос (как захвачено Fiddler):
GET https://STORAGE_ACCOUNT.table.core.windows.net/authors(PartitionKey='Beckett',RowKey='Molloy') HTTP/1.1 x-ms-date: Sun, 08 Sep 2013 06:31:14 GMT x-ms-version: 2012-02-12 Authorization: SharedKey STORAGE_ACCOUNT:1hWbr4aNq4JWCpNJY3rsLH1SkIyeFTJflbqyKMPQ1Gk= Accept-Charset: UTF-8 Accept: application/atom+xml,application/xml DataServiceVersion: 2.0;NetFx MaxDataServiceVersion: 2.0;NetFx Host: STORAGE_ACCOUNT.table.core.windows.net
Служба таблиц генерирует следующий ответ:
HTTP/1.1 200 OK Cache-Control: no-cache Content-Type: application/atom+xml;charset=utf-8 ETag: W/"datetime'2013-09-08T06%3A31%3A14.1579056Z'" Server: Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: f4bd4c77-6fb6-42a8-8dff-81ea8d28fa2e x-ms-version: 2012-02-12 Date: Sun, 08 Sep 2013 06:31:15 GMT Content-Length: 1108
Возвращенные объекты, в данном случае один объект, возвращаются в формате записи ATOM в теле ответа:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xml:base="https://STORAGE_ACCOUNT.table.core.windows.net/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/"datetime'2013-09-08T06%3A31%3A14.1579056Z'"" xmlns="http://www.w3.org/2005/Atom">
<id>https://STORAGE_ACCOUNT.table.core.windows.net/authors(PartitionKey='Beckett',RowKey='Molloy')</id>
<title type="text"></title>
<updated>2013-09-08T06:31:15Z</updated>
<author>
<name />
</author>
<link rel="edit" title="authors" href="authors(PartitionKey='Beckett',RowKey='Molloy')" />
<category term="STORAGE_ACCOUNT.authors" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:PartitionKey>Beckett</d:PartitionKey>
<d:RowKey>Molloy</d:RowKey>
<d:Timestamp m:type="Edm.DateTime">2013-09-08T06:31:14.1579056Z</d:Timestamp>
<d:Artist>Beckett</d:Artist>
<d:Title>Molloy
Molloy</d:Title>
</m:properties>
</content>
</entry>
API службы Blob
API Blob Service поддерживает следующие операции на уровне аккаунта:
API Blob Service поддерживает следующие операции контейнер уровня:
- Создать контейнер
- Удалить контейнер
- Получить контейнер ACL
- Получить свойства контейнера
- Получить метаданные контейнера
- Список BLOB-объектов
- Набор Контейнер ACL
- Установить метаданные контейнера
API Blob Service поддерживает следующие операции блоб уровня:
- Копировать Blob
- Удалить Blob
- Получить Blob
- Получить метаданные BLOB-объектов
- Получить Blob Properties
- Аренда Blob
- Положите Blob
- Установить метаданные BLOB-объектов
- Установить свойства BLOB-объектов
- Снимок Blob
API Blob Service поддерживает следующие операции на блок сгустков:
API Blob Service поддерживает следующие операции на странице сгустков:
В этом разделе приведены примеры операций Put Blob и Lease Blob.
Положите Blob
Служба BLOB-объектов и служба очередей используют другую форму проверки подлинности с общим ключом, чем служба таблиц, поэтому следует соблюдать осторожность при создании строки для подписи для авторизации. Тип blob, BlockBlob или PageBlob , должен быть указан в качестве заголовка запроса и, следовательно, появляется в строке авторизации.
public void PutBlob(String containerName, String blobName)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}/{1}", containerName, blobName);
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String content = "Andrew Carnegie was born in Dunfermline";
UTF8Encoding utf8Encoding = new UTF8Encoding();
Byte[] blobContent = utf8Encoding.GetBytes(content);
Int32 blobLength = blobContent.Length;
const String blobType = "BlockBlob";
String canonicalizedHeaders = String.Format(
"x-ms-blob-type:{0}\nx-ms-date:{1}\nx-ms-version:{2}",
blobType,
dateInRfc1123Format,
storageServiceVersion);
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath);
String stringToSign = String.Format(
"{0}\n\n\n{1}\n\n\n\n\n\n\n\n\n{2}\n{3}",
requestMethod,
blobLength,
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(AzureStorageConstants.BlobEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers.Add("x-ms-blob-type", blobType);
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.ContentLength = blobLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(blobContent, 0, blobLength);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
String ETag = response.Headers["ETag"];
}
}
Это генерирует следующий запрос:
PUT https://STORAGE_ACCOUNT.blob.core.windows.net/fife/dunfermline HTTP/1.1 x-ms-blob-type: BlockBlob x-ms-date: Sun, 08 Sep 2013 06:28:29 GMT x-ms-version: 2012-02-12 Authorization: SharedKey STORAGE_ACCOUNT:ntvh/lamVmikvwHhy6vRVBIh87kibkPlEOiHyLDia6g= Host: STORAGE_ACCOUNT.blob.core.windows.net Content-Length: 39 Expect: 100-continue Connection: Keep-Alive
Тело запроса:
Andrew Carnegie was born in Dunfermline
Служба BLOB-объектов генерирует следующий ответ:
HTTP/1.1 201 Created Transfer-Encoding: chunked Content-MD5: RYJnWGXLyt94l5jG82LjBw== Last-Modified: Sun, 08 Sep 2013 06:28:31 GMT ETag: "0x8D07A73C5704A86" Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: b74ef0a2-294d-4581-b8f1-6cda724bbdbf x-ms-version: 2012-02-12 Date: Sun, 08 Sep 2013 06:28:30 GMT
Аренда Blob
Служба BLOB-объектов позволяет пользователю арендовать большой двоичный объект за раз и таким образом получить блокировку записи на него. Вариант использования для этого — блокировка блоба страницы, используемого для хранения виртуального жесткого диска, поддерживающего записываемый диск Azure.
Пример LeaseBlob () в этом разделе демонстрирует тонкую проблему с созданием строк авторизации. URL содержит строку запроса, comp = lease . Вместо того, чтобы использовать это непосредственно при создании строки авторизации, она должна быть преобразована в comp: lease с двоеточием, заменяющим равный символ — см. В разделе modifyURL в примере. Кроме того, для операции Bloase Blob требуется использовать действие x-ms-lease-action, чтобы указать, приобретается ли договор аренды, продлевается, освобождается или нарушается.
public void LeaseBlob(String containerName, String blobName)
{
String requestMethod = "PUT";
String urlPath = String.Format("{0}/{1}?comp=lease", containerName, blobName);
String modifiedUrlPath = String.Format("{0}/{1}\ncomp:lease", containerName, blobName);
const Int32 contentLength = 0;
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String leaseAction = "acquire";
String leaseDuration = "60";
String canonicalizedHeaders = String.Format(
"x-ms-date:{0}\nx-ms-lease-action:{1}\nx-ms-lease-duration:{2}\nx-ms-version:{3}",
dateInRfc1123Format,
leaseAction,
leaseDuration,
storageServiceVersion);
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, modifiedUrlPath);
String stringToSign = String.Format(
"{0}\n\n\n{1}\n\n\n\n\n\n\n\n\n{2}\n{3}",
requestMethod,
contentLength,
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(AzureStorageConstants.BlobEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-lease-action", leaseAction);
request.Headers.Add("x-ms-lease-duration", leaseDuration);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.ContentLength = contentLength;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
String leaseId = response.Headers["x-ms-lease-id"];
}
}
Это генерирует следующий запрос:
PUT https://STORAGE_ACCOUNT.blob.core.windows.net/fife/dunfermline?comp=lease HTTP/1.1 x-ms-date: Sun, 08 Sep 2013 06:28:31 GMT x-ms-lease-action: acquire x-ms-lease-duration: 60 x-ms-version: 2012-02-12 Authorization: SharedKey rebus:+SQ5+RFZg3hUaws5XCRHxsDgXb1ycdRIz5EKyHJWP7s= Host: rebus.blob.core.windows.net Content-Length: 0
Служба BLOB-объектов генерирует следующий ответ:
HTTP/1.1 201 Created Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: 4b6ff77f-f885-4f74-803a-c92920d225c3 x-ms-version: 2012-02-12 x-ms-lease-id: b1320c2c-65ad-41d6-a7bd-85a4242c0ac5 Date: Sun, 08 Sep 2013 06:28:31 GMT Content-Length: 0
API службы очередей
API Queue Service поддерживает следующие операции очереди на уровне:
API Queue Service поддерживает следующие операции очереди на уровне:
API Queue Service поддерживает следующие операции на уровне сообщений:
В этом разделе приведены примеры операций «Поместить сообщение» и «Получить сообщение».
Поместить сообщение
Самым очевидным любопытством к Put Message является то, что он использует HTTP-глагол POST, а не PUT . Вероятно, проблема заключается во взаимодействии английского языка и стандарта HTTP, который гласит, что PUT должен быть идемпотентным и что операция Put Message явно не выполняется, поскольку каждый вызов просто добавляет другое сообщение в очередь. Несмотря на это, меня поймали, когда я не смог прочитать документацию достаточно хорошо — так что воспринимайте это как предупреждение.
Содержимое сообщения, отправляемого в очередь, должно быть отформатировано в указанной схеме XML, а затем должно быть закодировано в формате UTF8.
public void PutMessage(String queueName, String message)
{
String requestMethod = "POST";
String urlPath = String.Format("{0}/messages", queueName);
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String messageText = String.Format(
"<QueueMessage><MessageText>{0}</MessageText></QueueMessage>", message);
UTF8Encoding utf8Encoding = new UTF8Encoding();
Byte[] messageContent = utf8Encoding.GetBytes(messageText);
Int32 messageLength = messageContent.Length;
String canonicalizedHeaders = String.Format(
"x-ms-date:{0}\nx-ms-version:{1}",
dateInRfc1123Format,
storageServiceVersion);
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath);
String stringToSign = String.Format(
"{0}\n\n\n{1}\n\n\n\n\n\n\n\n\n{2}\n{3}",
requestMethod,
messageLength,
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(AzureStorageConstants.QueueEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.ContentLength = messageLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(messageContent, 0, messageLength);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
String requestId = response.Headers["x-ms-request-id"];
}
}
Это генерирует следующий запрос:
POST https://rebus.queue.core.windows.net/revolution/messages HTTP/1.1 x-ms-date: Sun, 08 Sep 2013 06:34:08 GMT x-ms-version: 2012-02-12 Authorization: SharedKey rebus:nyASTVWifnxHKnj2wXwuzzzXz5CxUBZj58SToV5QFK8= Host: rebus.queue.core.windows.net Content-Length: 76 Expect: 100-continue Connection: Keep-Alive
Тело запроса:
<QueueMessage><MessageText>Saturday in the cafe</MessageText></QueueMessage>
Служба очереди генерирует следующий ответ:
HTTP/1.1 201 Created Server: Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: 14c6e73b-15d9-480c-b251-c4c01b48e529 x-ms-version: 2012-02-12 Date: Sun, 08 Sep 2013 06:34:09 GMT Content-Length: 0
Получать сообщения
Операция «Получение сообщений», описанная в этом разделе, извлекает одно сообщение с тайм-аутом видимости сообщения по умолчанию, равным 30 секундам.
public void GetMessage(String queueName)
{
string requestMethod = "GET";
String urlPath = String.Format("{0}/messages", queueName);
String storageServiceVersion = "2012-02-12";
String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
String canonicalizedHeaders = String.Format(
"x-ms-date:{0}\nx-ms-version:{1}",
dateInRfc1123Format,
storageServiceVersion);
String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath);
String stringToSign = String.Format(
"{0}\n\n\n\n\n\n\n\n\n\n\n\n{1}\n{2}",
requestMethod,
canonicalizedHeaders,
canonicalizedResource);
String authorizationHeader = Utility.CreateAuthorizationHeader(stringToSign);
Uri uri = new Uri(AzureStorageConstants.QueueEndPoint + urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = requestMethod;
request.Headers.Add("x-ms-date", dateInRfc1123Format);
request.Headers.Add("x-ms-version", storageServiceVersion);
request.Headers.Add("Authorization", authorizationHeader);
request.Accept = "application/atom+xml,application/xml";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
using (StreamReader reader = new StreamReader(dataStream))
{
String responseFromServer = reader.ReadToEnd();
}
}
}
Это генерирует следующий запрос:
GET https://rebus.queue.core.windows.net/revolution/messages HTTP/1.1 x-ms-date: Sun, 08 Sep 2013 06:34:11 GMT x-ms-version: 2012-02-12 Authorization: SharedKey rebus:K67XooYhokw0i0AlCzYQ4GeLLrJih1r1vSqiO9DBo0c= Accept: application/atom+xml,application/xml Host: rebus.queue.core.windows.net
Служба очереди генерирует следующий ответ:
HTTP/1.1 200 OK Content-Type: application/xml Server: Windows-Azure-Queue/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: efb21a86-7d66-47fd-b13d-7aa74fce0568 x-ms-version: 2012-02-12 Date: Sun, 08 Sep 2013 06:34:12 GMT Content-Length: 484
Сообщение возвращается в теле ответа следующим образом:
<?xml version="1.0" encoding="utf-8"?><QueueMessagesList><QueueMessage><MessageId>05fd902f-6031-4ef4-8298-ef3844ec3bc6</MessageId><InsertionTime>Sun, 08 Sep 2013 06:34:11 GMT</InsertionTime><ExpirationTime>Sun, 15 Sep 2013 06:34:11 GMT</ExpirationTime><DequeueCount>1</DequeueCount><PopReceipt>AgAAAAMAAAAAAAAAAL+zgF2szgE=</PopReceipt><TimeNextVisible>Sun, 08 Sep 2013 06:34:43 GMT</TimeNextVisible><MessageText>Saturday in the cafe</MessageText></QueueMessage></QueueMessagesList>
Я заметил, что некоторые спецификаторы новой строки в строках (\ n) были потеряны, когда блог был автоматически перенесен из Windows Live Spaces в WordPress. Я положил их обратно, но, возможно, я пропустил некоторые. Следовательно, в случае возникновения проблемы вы должны проверить переводы строк в canonicalizedHeaders и stringToSign .