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

来源:互联网 发布:德华软件 编辑:程序博客网 时间:2024/03/28 22:27

 

公告:本博客为微软云计算中文博客的镜像博客。部分文章因为博客兼容性问题,会影响阅读体验。如遇此情况,请访问原博客

 

 


 

本文是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。由于如何添加ContainerBlob的方法我们已经在Windows Azure入门教学系列 ():使用Blob Storage中提过,在此不赘述。读者可以按照Windows Azure入门教学系列 ():使用Blob Storage中的代码创建名为helloworldcontainerContainer和名为myfileBlob(只需注释掉删除Blob的代码并运行程序即可)

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

首先,请确保Storage Emulator已经启动。我们可以找到管理器的进程手动启动或者让Visual Studio 2010帮助我们启动他。

右击工具栏中Windows Azure模拟器的图标,选择”Show Storage Emulator UI”。弹出如下图所示的窗口:

 

 

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

确认完毕后启动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 = 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 = @"http://127.0.0.1:10000/devstoreaccount1";

const string accountname = "devstoreaccount1";

const string key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";

 

这三行代码定义了Blob Storage服务端口,我们使用的账户名和密码。由于使用的是本地模拟的Storage,所以必须使用固定的端口地址,用户名以及密码。可以参考Development StorageWindows Azure Storage Services的不同之处获取更多信息。

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

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

 

步骤六:运行程序

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

原创粉丝点击