Windows Azure入门教学系列 (七):使用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 = @"https://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 = newSystem.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 = @"https://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的信息: