本文是 Windows Azure 入门教学 的第七篇文章。
本文将会介绍如何使用REST API来直接访问 Storage Service。
在前三篇教学中,我们已经学习了使用 Windows Azure SDK所提供的 StorageClient来使用 Blob Storage, Queue Storage以及 Table Storage的基本方法。我们在前几篇教学中也提及最终 StorageClient也是通过发送 REST请求来与服务器端通信的。
在这篇教学中,我们会以 Blob Storage为例,说明如何使用 REST API直接与服务器进行通信。需要说明的是,这篇教学中使用的是 C#语言。但是由于 REST API实际上是通过 HTTP发送的 HTTP消息,使用其他语言的工程师同样可以参考代码逻辑了解如何构造 HTTP消息以便在其他编程语言中使用。
在开始本教学之前,请确保你从Windows Azure 平台下载下载并安装了最新的 Windows Azure开发工具。 本教学使用 Visual Studio 2010作为开发工具。
在本教学中我们将使用 List Blobs API,欲了解详细信息,请参见List Blobs。
该 API的作用是返回给定的 Container中的 Blob信息。为了测试我们的代码我们首先需要有一个已经创建的 Container并且向其中添加至少一个 Blob。由于如何添加 Container和 Blob的方法我们已经在 Windows Azure入门教学系列 (四 ):使用 Blob Storage 中提过,在此不赘述。读者可以按照 Windows Azure入门教学系列 (四 ):使用 Blob Storage 中的代码创建名为 helloworldcontainer的 Container和名为 myfile的 Blob。 (只需注释掉删除 Blob的代码并运行程序即可 )
首先,请确保 Storage Emulator已经启动。我们可以找到管理器的进程手动启动或者让 Visual Studio 2010帮助我们启动他。
右击工具栏中 Windows Azure模拟器的图标,选择” Show Storage Emulator UI”。弹出如下图所示的窗口:
我们要关注的是 Service management中 Blob所在的一行。要确保 Status为 Running。
确认完毕后启动 Visual Studio 2010,并且新建一个 Console项目。
请在项目属性页里确认项目的 Target framework的值是 .NET Framework 4或 .NET Framework 3.5。然后在 Console项目中添加对 System.Web程序集的引用。该程序集安装在 GAC中。在 .NET标签下能够找到该程序集。我们将使用该程序集来发送 HTTP请求和接受 HTTP消息。
首先在项目中的 Program.cs中引用命名空间:
using System.IO; using System.Collections.Specialized; using System.Collections; using System.Web; using System.Net;然后在 Program.cs中添加如下代码 :
class Program { internal class CanonicalizedString { private StringBuilder canonicalizedString = new StringBuilder (); internal CanonicalizedString(string initialElement) { this .canonicalizedString.Append(initialElement); } internal void AppendCanonicalizedElement(string element) { this .canonicalizedString.Append("/n" ); this .canonicalizedString.Append(element); } internal string Value { get { return this .canonicalizedString.ToString(); } } } const string bloburi = @"http://127.0.0.1:10000/devstoreaccount1" ; const string accountname = "devstoreaccount1" ; const string key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" ; const string method = "GET" ; static void Main(string [] args) { string AccountName = accountname; string AccountSharedKey = key; string Address = bloburi; string container = "helloworldcontainer" ; // 创建请求字符串 string QueryString = "?restype=container&comp=list" ; Uri requesturi = new Uri (Address + "/" + container + QueryString); string MessageSignature = "" ; // 创建 HttpWebRequest 类 HttpWebRequest Request = (HttpWebRequest )HttpWebRequest .Create(requesturi.AbsoluteUri); Request.Method = method; Request.ContentLength = 0; Request.Headers.Add("x-ms-date" , DateTime .UtcNow.ToString("R" )); Request.Headers.Add("x-ms-version" , "2009-09-19" ); // 开始创建签名 MessageSignature += "GET/n" ; // Verb MessageSignature += "/n" ; // Content-Encoding MessageSignature += "/n" ; // Content-Language MessageSignature += "/n" ; // Content-Length MessageSignature += "/n" ; // Content-MD5 MessageSignature += "/n" ; // Content-Type MessageSignature += "/n" ; // Date MessageSignature += "/n" ; // If-Modified-Since MessageSignature += "/n" ; // If-Match MessageSignature += "/n" ; // If-None-Match MessageSignature += "/n" ; // If-Unmodified-Since MessageSignature += "/n" ; // Range // CanonicalizedHeaders ArrayList list = new ArrayList (); foreach (string str in Request.Headers.Keys) { if (str.ToLowerInvariant().StartsWith("x-ms-" , StringComparison .Ordinal)) { list.Add(str.ToLowerInvariant()); } } list.Sort(); foreach (string str2 in list) { StringBuilder builder = new StringBuilder (str2); string str3 = ":" ; foreach (string str4 in GetHeaderValues(Request.Headers, str2)) { string str5 = str4.Replace("/r/n" , string .Empty); builder.Append(str3); builder.Append(str5); str3 = "," ; } MessageSignature += builder.ToString() + "/n" ; } MessageSignature += GetCanonicalizedResourceVersion2(requesturi, AccountName); // 开始创建签名 byte [] SignatureBytes = System.Text.Encoding .UTF8.GetBytes(MessageSignature); System.Security.Cryptography.HMACSHA256 SHA256 = new System.Security.Cryptography.HMACSHA256 (Convert .FromBase64String(AccountSharedKey)); // 创建 Authorization HTTP 消息头的值 String AuthorizationHeader = "SharedKey " + AccountName + ":" + Convert .ToBase64String(SHA256.ComputeHash(SignatureBytes)); // 把编码后的签名加入到 Authorization HTTP 消息头中 Request.Headers.Add("Authorization" , AuthorizationHeader); // 获取返回消息 using (HttpWebResponse response = (HttpWebResponse )Request.GetResponse()) { if (response.StatusCode == HttpStatusCode .OK) { // 服务器返回成功消息 using (Stream stream = response.GetResponseStream()) { using (StreamReader sr = new StreamReader (stream)) { var s = sr.ReadToEnd(); // 输出返回消息 Console .WriteLine(s); } } } else { // 这里可以抛出异常信息 } } Console .ReadLine(); } static ArrayList GetHeaderValues(NameValueCollection headers, string headerName) { ArrayList list = new ArrayList (); string [] values = headers.GetValues(headerName); if (values != null ) { foreach (string str in values) { list.Add(str.TrimStart(new char [0])); } } return list; } static string GetCanonicalizedResourceVersion2(Uri address, string accountName) { StringBuilder builder = new StringBuilder ("/" ); builder.Append(accountName); builder.Append(address.AbsolutePath); CanonicalizedString str = new CanonicalizedString (builder.ToString()); NameValueCollection values = HttpUtility .ParseQueryString(address.Query); NameValueCollection values2 = new NameValueCollection (); foreach (string str2 in values.Keys) { ArrayList list = new ArrayList (values.GetValues(str2)); list.Sort(); StringBuilder builder2 = new StringBuilder (); foreach (object obj2 in list) { if (builder2.Length > 0) { builder2.Append("," ); } builder2.Append(obj2.ToString()); } values2.Add((str2 == null ) ? str2 : str2.ToLowerInvariant(), builder2.ToString()); } ArrayList list2 = new ArrayList (values2.AllKeys); list2.Sort(); foreach (string str3 in list2) { StringBuilder builder3 = new StringBuilder (string .Empty); builder3.Append(str3); builder3.Append(":" ); builder3.Append(values2[str3]); str.AppendCanonicalizedElement(builder3.ToString()); } return str.Value; } }步骤五:观察并分析代码
我们首先观察下面三行代码:
const string bloburi = @"http://127.0.0.1:10000/devstoreaccount1" ; const string accountname = "devstoreaccount1" ; const string key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" ;
这三行代码定义了 Blob Storage服务端口,我们使用的账户名和密码。由于使用的是本地模拟的 Storage,所以必须使用固定的端口地址,用户名以及密码。可以参考Development Storage与 Windows Azure Storage Services的不同之处获取更多信息。
在代码中,我们使用 HttpWebRequest 构造并发送 HTTP请求。由于我们的例子中使用的是 List Blobs API我们需要查阅List Blobs对该 API的规定构造符合规定的 HTTP请求。
在规定中说明了如果该 container没有被设置为允许匿名访问,那么必须由于账户拥有者调用该 API才能返回结果。要这样做必须添加 Authorization HTTP消息头。我们必须严格按照Authentication Schemes规定的格式来生成该 HTTP消息头。
如果一切正常,你将会看到 Console程序输出如下信息,内容为名为 helloworld的 Container中所有 Blob的信息: