Windows Azure入门教学系列 (七):使用REST API访问Storage Service

Windows Azure入门教学系列 (七):使用REST API访问Storage Service

  • Comments 0

本文是Windows Azure入门教学的第七篇文章。

本文将会介绍如何使用REST API来直接访问Storage Service

在前三篇教学中,我们已经学习了使用Windows Azure SDK所提供的StorageClient来使用Blob Storage, Queue Storage以及Table Storage的基本方法。我们在前几篇教学中也提及最终StorageClient也是通过发送REST请求来与服务器端通信的。在这篇教学中,我们会以Blob Storage为例,说明如何使用RESTAPI直接与服务器进行通信。需要说明的是,这篇教学中使用的是C#语言。但是由于RESTAPI实际上是通过HTTP发送的HTTP消息,使用其他语言的工程师同样可以参考代码逻辑了解如何构造HTTP消息���便在其他编程语言中使用。

要继续本教学,请确保自己的机体上安装了最新版的Windows Azure Tools for Microsoft Visual Studio。当前的最新版(20102月)可以从以下站点下载:

http://www.microsoft.com/downloads/details.aspx?familyid=5664019E-6860-4C33-9843-4EB40B297AB6&displaylang=en

步骤一:准备工作

在本教学中我们将使用List Blobs API:

http://msdn.microsoft.com/en-us/library/dd135734.aspx

该API的作用是返回给定的Container中的Blob信息。为了测试我们的代码我们首先需要有一个已经创建的Container并且向其中添加至少一个Blob。由于如何添加Container和Blob的方法我们已经在Windows Azure入门教学系列 (四):使用Blob Storage中提过,在此不赘述。读者可以按照Windows Azure入门教学系列 (四):使用Blob Storage中的代码创建名为helloworldcontainer的Container和名为myfile的Blob。(只需注释掉删除Blob的代码并运行程序即可)

步骤二:创建解决方案和项目

由于我们要在本地模拟环境下测试Table Storage,首先,请确保Development Storage的管理器程序已经启动。我们可以找到管理器的进程手动启动或者让Visual Studio帮助我们启动他。你可以参考第一篇教学文章来启动他:

http://blogs.msdn.com/azchina/archive/2010/02/09/windows-azure-webrole.aspx

右击工具栏中Development Fabric的图标,选择”Show Development Storage UI”。弹出如下图所示的窗口:

我们要关注的是Service management中Blob所在的一行。要确保StatusRunning

确认完毕后启动Visual Studio,并且新建一个Console项目。

 

步骤三:添加程序集引用

Console项目中添加对System.Web程序集的引用。该程序集安装在GAC中。在.NET标签下能够找到该程序集。我们将使用该程序集来发送HTTP请求和接受HTTP消息。

步骤四:添加代码

首先在项目中的Program.cs中引用命名空间:

using System.IO;
using System.Collections.Specialized;
using System.Collections;
using System.Web;

然后在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);

            // HMAC-SHA256对签名编码 

            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,所以必须使用固定的端口地址,用户名以及密码。可以参考MSDN文档获取更多信息:

http://msdn.microsoft.com/en-us/library/dd320275.aspx

在代码中,我们使用HttpWebRequest 构造并发送HTTP请求。由于我们的例子中使用的是List Blobs API我们需要查阅MSDN对该API的规定构造符合规定的HTTP请求:

http://msdn.microsoft.com/en-us/library/dd135734.aspx

在规定中说明了如果该container没有被设置为允许匿名访问,那么必须由于账户拥有者调用该API才能返回结果。要这样做必须添加Authorization HTTP消息头。我们必须严格按照下面一篇文档规定的格式来生成该HTTP消息头:

http://msdn.microsoft.com/en-us/library/dd179428.aspx

步骤六:运行程序

如果一切正常,你将会看到Console程序输出如下信息,内容为名为helloworld的Container中所有Blob的信息

Leave a Comment
  • Please add 2 and 2 and type the answer here:
  • Post