使用REST API访问Storage Service
本文是 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的信息: