Примеры в этом посте были обновлены в сентябре для работы с текущей версией 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 .