<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blog2.darkthread.net/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CHT"><title type="html">黑暗執行緒</title><subtitle type="html" /><id>http://blog2.darkthread.net/blogs/darkthreadtw/atom.aspx</id><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/blogs/darkthreadtw/default.aspx" /><link rel="self" type="application/atom+xml" href="http://blog2.darkthread.net/blogs/darkthreadtw/atom.aspx" /><generator uri="http://communityserver.org" version="3.1.20917.1142">Community Server</generator><updated>2011-12-13T18:11:00Z</updated><entry><title>多想兩分鐘，你可以不用 validateRequest="false"(WebForm版)</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-02-01-pass-request-validation-in-webform.aspx" /><id>http://blog2.darkthread.net/post-2012-02-01-pass-request-validation-in-webform.aspx</id><published>2012-02-01T14:14:03Z</published><updated>2012-02-01T14:14:03Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;接續&lt;a href="http://blog.darkthread.net/post-2012-02-01-pass-request-validation.aspx"&gt;前一篇&lt;/a&gt;不關閉validateRequest下允許傳送XML內容的議題，有人問起，我才想到該文只示範了AJAX做法，壓根忘了提WebForm環境下應如何處理。&lt;/p&gt;  &lt;p&gt;以下是我會採用的處理方式。原理上還是透過encodeURIComponent()及HttpUtility.UrlDecode()分別在Client端與Server端加解碼，只是要額外動些手腳:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;在網頁上放一個&amp;lt;asp:HiddenField&amp;gt;作為實際的傳值容器。&lt;/li&gt;    &lt;li&gt;供使用者輸入的&amp;lt;textarea&amp;gt;或&amp;lt;input type=&amp;quot;text&amp;quot;&amp;gt;定義成純Client端元素，並記得只指定id，不要指定name屬性，不然它會一併被Postback引發檢核錯誤，徒增麻煩。 &lt;/li&gt;    &lt;li&gt;在表單的Client onsubmit事件中，取出textarea內容，經encodeURIComponent()後存入&amp;lt;asp:HiddenField&amp;gt;中，接著就可以放心送出表單囉! &lt;/li&gt;    &lt;li&gt;Server端在IsPostBack時用HttpUtility.UrlDecode(theHiddenField.Value)就能輕鬆取值。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;以下是程式範例:&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; %&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsPostBack)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.ContentType = &lt;span class="str"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            Response.Write(&lt;span class="str"&gt;&amp;quot;Data=&amp;quot;&lt;/span&gt; + HttpUtility.UrlDecode(hdnXml.Value));&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.End();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;html&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;head runat=&lt;span class="str"&gt;&amp;quot;server&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;title&amp;gt;MyLab&amp;lt;/title&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        body,input { font-size: 9pt; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js&amp;#39;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;   &lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text(&lt;span class="str"&gt;&amp;quot;&amp;lt;data&amp;gt;Text&amp;lt;/data&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//掛上onsubmit事件，表單送出前，將textarea的內容編碼後寫入hidden&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#form1&amp;quot;&lt;/span&gt;).submit(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                $(&lt;span class="str"&gt;&amp;quot;#hdnXml&amp;quot;&lt;/span&gt;).val(encodeURIComponent($(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text()));&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;&amp;lt;!--用純Client端的textarea或input讓使用者輸入XML，&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="rem"&gt;        記得只設id，不要設name，以免PostBack時被一併送回--&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;txtXml&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;cols&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;20&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;rows&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:HiddenField&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hdnXml&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;EnableViewState&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Button&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnSubmit&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Submit&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;PS: 當然，你也可選擇不另增設HiddenField，直接在onsubmit時取出&amp;lt;textarea&amp;gt;內容，encodeURIComponent()後再寫回&amp;lt;textarea&amp;gt;。不過省下這點工，卻必須面對使用者回上頁時&amp;lt;textarea&amp;gt;變成編碼後內容的燙手山芋，個人認為並不划算，建議別自找麻煩。&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8767" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="ASP.NET" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx" /><category term="Security" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Security/default.aspx" /><category term="ASP.NET保安" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET_DD4F895B_/default.aspx" /></entry><entry><title>多想兩分鐘，你可以不用 validateRequest="false"</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-02-01-pass-request-validation.aspx" /><id>http://blog2.darkthread.net/post-2012-02-01-pass-request-validation.aspx</id><published>2012-02-01T00:21:30Z</published><updated>2012-02-01T00:21:30Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;先來看以下的程式，網頁上放了一個&amp;lt;textarea&amp;gt;及&amp;lt;input type=&amp;quot;button&amp;quot;&amp;gt;，按鈕後以$.post()方式將&amp;lt;textarea&amp;gt;的內容送至ASP.NET Server端程式，在Page_Load中讀取Request[&amp;quot;data&amp;quot;]並顯示出來，另外並透過$.ajaxSetup()指定error錯誤事件函數，捕捉並顯示伺服器端的錯誤資訊。&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; %&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request[&lt;span class="str"&gt;&amp;quot;mode&amp;quot;&lt;/span&gt;] == &lt;span class="str"&gt;&amp;quot;ajax&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.ContentType = &lt;span class="str"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            Response.Write(&lt;span class="str"&gt;&amp;quot;Data=&amp;quot;&lt;/span&gt; + Request[&lt;span class="str"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;]);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.End();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;html&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;head runat=&lt;span class="str"&gt;&amp;quot;server&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;title&amp;gt;MyLab&amp;lt;/title&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        body,input { font-size: 9pt; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js&amp;#39;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;   &lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text(&lt;span class="str"&gt;&amp;quot;&amp;lt;data&amp;gt;Text&amp;lt;/data&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//發生錯誤時顯示錯誤訊息&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $.ajaxSetup({&lt;/pre&gt;

    &lt;pre&gt;                error: &lt;span class="kwrd"&gt;function&lt;/span&gt; (xhr, textStatus, err) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    alert(&lt;span class="str"&gt;&amp;quot;ERROR: &amp;quot;&lt;/span&gt; + err + &lt;span class="str"&gt;&amp;quot;=&amp;gt;&amp;quot;&lt;/span&gt; + xhr.responseText);&lt;/pre&gt;

    &lt;pre&gt;                }&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//以$.post方式將textarea的內容送至Server端&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#btnAjaxPost&amp;quot;&lt;/span&gt;).click(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                $.post(&lt;span class="str"&gt;&amp;quot;?mode=ajax&amp;quot;&lt;/span&gt;, { data: $(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text() },&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;function&lt;/span&gt; (r) { alert(r); }&lt;/pre&gt;

    &lt;pre&gt;                );&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;txtXml&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;cols&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;20&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;rows&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;button&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnAjaxPost&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Ajax Post&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;老鳥們應該已嗅出異常: 程式會出錯!!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8750/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;由於我故意在&amp;lt;textarea&amp;gt;中塞入了XML內容，而ASP.NET 2.0起增加了&lt;a href="http://www.asp.net/whitepapers/request-validation"&gt;Request Validation&lt;/a&gt;的機制，防範惡意人士在URL或PostBack內容中夾雜HTML標籤進行XSS攻擊。很多人遇到這個狀況，直覺反應就是毫不猶豫將&lt;a href="http://msdn.microsoft.com/en-us/library/ydy4x04a(v=vs.71).aspx"&gt;@Page&lt;/a&gt; validateRequest設成false(甚至一不做二不休，在web.config設定把整個網站的requestValidate都關掉)，程式不再出錯，就開開心心快快樂樂繼續寫下去。&lt;/p&gt;

&lt;p&gt;身為一個輕度資安偏執者，我反對這種處理方式!! 多想兩分鐘，你可以不要validateRequest=&amp;quot;false&amp;quot;。&lt;/p&gt;

&lt;p&gt;廚師帶著鍋杓要進總統府外燴，經過金屬探測門安檢時警報大作，解決之道是把金屬探測器拆掉??&lt;/p&gt;

&lt;p&gt;不是吧? 針對鍋杓的特例另外處理才是王道，但在資訊系統中無法通過檢核就把防護關掉眼不見為淨的例子比比皆是... (檔案拒絕存取就一律開Everyone、程式在Windows 7跑不了就&lt;a href="http://blog.darkthread.net/post-2009-05-21-why-turn-off-vista-uac.aspx"&gt;關UAC&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;依我的看法:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;font color="#f79646"&gt;應保留requestValidate=&amp;quot;true&amp;quot;擋下大部分未知的可能攻擊，針對確實會包含XML/HTML資料且另有內容安全檢核程序的情境，再設法繞道而行&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;才是安全的策略!&lt;/p&gt;

&lt;p&gt;要怎麼讓HTML/XML內容傳送時避免被Request Validation機制阻攔呢? 其實不難，Client端在送出資料前進行編碼，Server端在收到資料進行解碼還原即可。而借用&lt;a href="http://www.w3schools.com/jsref/jsref_encodeuricomponent.asp"&gt;encodeURIComponent()&lt;/a&gt;應是最簡單的寫法了，例如: &lt;/p&gt;

&lt;p&gt;在$.post()時改成 
  &lt;br /&gt;&lt;font color="#00ff00"&gt;$.post(&amp;quot;?mode=ajax&amp;quot;, { data: &lt;font style="background-color:#ffff00;" color="#ff0000"&gt;encodeURIComponent(&lt;/font&gt;$(&amp;quot;#txtXml&amp;quot;).text()&lt;font style="background-color:#ffff00;" color="#ff0000"&gt;)&lt;/font&gt; }&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;在Page_Load()時改成 
  &lt;br /&gt;&lt;font color="#00ff00"&gt;Response.Write(&amp;quot;Data=&amp;quot; + &lt;font style="background-color:#ffff00;" color="#ff0000"&gt;HttpUtility.UrlDecode(&lt;/font&gt;Request[&amp;quot;data&amp;quot;]&lt;font style="background-color:#ffff00;" color="#ff0000"&gt;)&lt;/font&gt;);&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;前後端一搭一唱，就可以在不觸發警告的情況下完成XML或HTML資料傳送。但必須牢記，加上了這段設計，意味著&lt;font color="#f79646"&gt;駭客就能在Request[&amp;quot;data&amp;quot;]中設法摻入HTML標籤，一定要加上邏輯杜絕Request[&amp;quot;data&amp;quot;]夾帶HTML標籤引來XSS攻擊的風險&lt;/font&gt;。&lt;/p&gt;

&lt;p&gt;encodeURIComponent()的做法有個小缺點，就是編碼後內容會膨脹。例如原本的&amp;lt;data&amp;gt;Text&amp;lt;/data&amp;gt;會變成%253Cdata%253E%25E5%259C%258B%25E5%25AE%25B6%253C%252Fdata%253E。若要改善，需另覓其他的編碼規則，或在部分情境可考量將XML內容直接當成Post的內容主體(&lt;a href="http://blog.darkthread.net/post-2009-10-02-jquery-ajax-xml-and-aspx.aspx"&gt;補充參考&lt;/a&gt;)。&lt;/p&gt;

&lt;p&gt;簡單範例如下，Client端改用$.ajax()方式傳送，指定type=&amp;quot;POST&amp;quot;, contentType=&amp;quot;text/xml&amp;quot;(或是text/plain亦可), 另外還要指定processData=false，即可將data內容當成POST本體，如此亦可避開Request Validation，達成相同目的。&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; %&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request[&lt;span class="str"&gt;&amp;quot;mode&amp;quot;&lt;/span&gt;] == &lt;span class="str"&gt;&amp;quot;ajax&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.ContentType = &lt;span class="str"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//使用StreamReader讀入Request.InputStream的所有內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; sr = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.IO.StreamReader(Request.InputStream);&lt;/pre&gt;

    &lt;pre&gt;            Response.Write(&lt;span class="str"&gt;&amp;quot;Data=&amp;quot;&lt;/span&gt; + sr.ReadToEnd());&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Response.End();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;html&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;head runat=&lt;span class="str"&gt;&amp;quot;server&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        body,input { font-size: 9pt; }&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js&amp;#39;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;   &lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;            $(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text(&lt;span class="str"&gt;&amp;quot;&amp;lt;data&amp;gt;Text&amp;lt;/data&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//以$.post方式將textarea的內容送至Server端&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            $(&lt;span class="str"&gt;&amp;quot;#btnAjaxPost&amp;quot;&lt;/span&gt;).click(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                $.ajax({&lt;/pre&gt;

    &lt;pre&gt;                    url: &lt;span class="str"&gt;&amp;quot;?mode=ajax&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    type: &lt;span class="str"&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;                    contentType: &lt;span class="str"&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;, &lt;span class="rem"&gt;//指定格式為XML&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    data: $(&lt;span class="str"&gt;&amp;quot;#txtXml&amp;quot;&lt;/span&gt;).text(),&lt;/pre&gt;

    &lt;pre&gt;                    processData: &lt;span class="kwrd"&gt;false&lt;/span&gt;, &lt;span class="rem"&gt;//指定直接傳送內容不做處理&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    success: &lt;span class="kwrd"&gt;function&lt;/span&gt; (r) { alert(r); }&lt;/pre&gt;

    &lt;pre&gt;                });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;txtXml&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;cols&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;20&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;rows&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;textarea&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;button&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnAjaxPost&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Ajax Post&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8763" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="ASP.NET" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx" /><category term="Security" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Security/default.aspx" /><category term="jQuery" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/jQuery/default.aspx" /></entry><entry><title>TIPS-在多執行緒環境更新共用資料物件</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-31-dictionary-thread-safe.aspx" /><id>http://blog2.darkthread.net/post-2012-01-31-dictionary-thread-safe.aspx</id><published>2012-01-31T14:47:00Z</published><updated>2012-01-31T14:47:00Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;有一段小時候寫的程式，運行多年無恙，卻在今天發生爆炸! &lt;/p&gt;  &lt;p&gt;看似正常的更新Dictationary邏輯，卻在Dictoinary.Add時冒出&amp;quot;Index was outside the bounds of the array.&amp;quot;錯誤:&lt;/p&gt;  &lt;p&gt;&lt;font color="#00ff00"&gt;if (Cache.ContainsKey(key)) Cache.Remove(key);      &lt;br /&gt;Cache.Add(key,&amp;nbsp; someData);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Cache的型別為Dictionary&amp;lt;string, string&amp;gt;，程式先檢查該Key值是否存在，若存在先將該Key資料移除，接著存入新資料。由於是在網站環境，程式碼有可能被多個Request以多執行緒方式同時呼叫，而不知是年幼無知或一時糊塗，這段更新資料邏輯並沒有加上執行緒安全防護(Thread-Safe)，可能就是導致爆炸的原因。但直覺上會爆出的狀況是剛Remove()還來不及Add()前，另一個Request(另一條執行緒)插隊先Add()相同Key值的資料而導致&amp;quot;An item with the same key has already been added&amp;quot;錯誤，但冒出IndexOutOfRangeException就讓人有些不解，莫非這也是多執行緒寫入失控引發的現象之一?&lt;/p&gt;  &lt;p&gt;設計了一個驗證測試，我使用以下做法在static Dictionary&amp;lt;string, string&amp;gt;新增相同Key值的資料，用Thread.Sleep()隨機時間的方式模擬實務上自然湧入的Request。&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Threading;&lt;/pre&gt;

    &lt;pre&gt;&amp;nbsp;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TestRunner1&lt;/pre&gt;

    &lt;pre&gt;{&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Cache =&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//警告: 以下為未考慮Thread-Safe的寫法&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateCache(&lt;span class="kwrd"&gt;string&lt;/span&gt; key, &lt;span class="kwrd"&gt;string&lt;/span&gt; data)&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (Cache.ContainsKey(key))&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Cache.Remove(key);&lt;/pre&gt;

    &lt;pre&gt;        Cache.Add(key, data);&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; Random rnd = &lt;span class="kwrd"&gt;new&lt;/span&gt; Random();&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//進行測試，更新Cache的同一項目&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Test(&lt;span class="kwrd"&gt;int&lt;/span&gt; i)&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//產生隨機延遲，設法逼近一般實務情境&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Thread.Sleep(rnd.Next(5));&lt;/pre&gt;

    &lt;pre&gt;        UpdateCache(&lt;span class="str"&gt;&amp;quot;MyCache&amp;quot;&lt;/span&gt;, rnd.Next().ToString());&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Console.WriteLine(&lt;span class="str"&gt;&amp;quot;Done-{0:0000}&amp;quot;&lt;/span&gt;, i);&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;呼叫端以ThreadPool多執行方式呼叫TestRunner1.Test()，並捕捉IndexOutOfRangeException並設定Visual Studio偵錯中斷點，再使用浮動監看視窗觀察Dictionary遭多執行緒蹂躙後的樣子。多試幾次，果然看到許多奇異現象:&lt;/p&gt;

&lt;p&gt;Index was outside of the bounds of the array!&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8758/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Count變成負數! 酷吧?
  &lt;br /&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8759/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;雜湊表型別中出現Key值相同的奇景!! 沒見過吧?&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8760/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;啊哈!! 我抓到.NET Framework Dictionary物件的Bug嗎? 才不是哩!! &lt;strike&gt;本草綱目&lt;/strike&gt;&lt;a href="http://msdn.microsoft.com/zh-tw/library/3fcwy8h6.aspx"&gt;MSDN文件&lt;/a&gt;有記載:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;只要未修改集合，Dictionary&amp;lt;TKey, TValue&amp;gt;.KeyCollection 可同時支援多個讀取器 (Reader)。 即便如此，透過集合的列舉基本上並非安全的執行緒程序。 若要確保列舉期間的執行緒安全，您可以在整個列舉期間鎖定集合。 若要允許多重執行緒存取集合以進行讀寫，您必須實作自己的同步處理。&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;A Dictionary&amp;lt;TKey, TValue&amp;gt;.KeyCollection can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;簡單來說，Dictionary&amp;lt;TKey, TValue&amp;gt;在多執行緒下讀取沒問題，但若打算在多個執行緒中同時更新或是跑foreach，就必須自行處理鎖定及同步議題。在這個測試案例中，最簡單的做法在UpdateCache()時加上一行&lt;a href="http://msdn.microsoft.com/zh-tw/library/c5kehkcz.aspx"&gt;lock&lt;/a&gt;:&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//簡易的單層鎖定機制&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateCache(&lt;span class="kwrd"&gt;string&lt;/span&gt; key, &lt;span class="kwrd"&gt;string&lt;/span&gt; data)&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;font&gt;&lt;span class="kwrd"&gt;lock&lt;/span&gt; (Cache)&lt;/font&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (Cache.ContainsKey(key))&lt;/pre&gt;

    &lt;pre class="alt"&gt;                Cache.Remove(key);&lt;/pre&gt;

    &lt;pre&gt;            Cache.Add(key, data);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;當所有執行緒以Cache為對象進行鎖定後，可迫使所有更新動作排隊依序執行，防止多個執行緒搶著更新同一個物件衍生的不可預期後果。(在使用複雜查詢產生快取資料的情境，若要避免鎖定及重複執行拖累效能，還要進一步考量使用&lt;a href="http://en.wikipedia.org/wiki/Double-checked_locking"&gt;雙重檢查鎖定&lt;/a&gt;之類的設計模式，這部分未來有機會再另文討論) 而同樣的原理也適用ASP.NET Cache物件的新增或更新操作上，一樣是全Process只有一份，一樣會有多個Request多執行緒同時執行，故最好也加上同步化或鎖定機制以防止多執行緒打架。&lt;/p&gt;

&lt;p&gt;另外，如果你的專案使用的是.NET 4.0，則有個好消息: .NET 4.0新增了System.Collections.Concurrent命名空間中，多了ConcurrentDictionary, ConcurrentQueue, ConcurrentStack... 等等五個內建支援多緒存取的&lt;a href="http://msdn.microsoft.com/zh-tw/library/dd997373.aspx"&gt;安全執行緒集合類別&lt;/a&gt;，省卻了自行處理lock的麻煩，可多加利用。&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; ConcurrentDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Cache =&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;new&lt;/span&gt; ConcurrentDictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//改用ConcurrentDictionary&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UpdateCache(&lt;span class="kwrd"&gt;string&lt;/span&gt; key, &lt;span class="kwrd"&gt;string&lt;/span&gt; data)&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//不存在就新增、否則用更新的&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Cache.AddOrUpdate(key, data, &lt;/pre&gt;

    &lt;pre&gt;          &lt;span class="rem"&gt;//第三個參數是當key值存在時，要更新的內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//本例中，則固定以data值覆寫之&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            (k, v) =&amp;gt; data);&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8762" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term=".NET" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/.NET/default.aspx" /><category term="C# 4.0" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/C_2300_+4.0/default.aspx" /></entry><entry><title>【茶包射手日記】使用IE Dev Tools追查Javascript效能問題</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-18-ie-dev-tools-profiler-case.aspx" /><id>http://blog2.darkthread.net/post-2012-01-18-ie-dev-tools-profiler-case.aspx</id><published>2012-01-18T15:44:55Z</published><updated>2012-01-18T15:44:55Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;同事回報某個網頁出現&amp;quot;&lt;font color="#00ff00"&gt;在IE8下，使用jquery.blockUI遮蔽某個div後，&amp;lt;input type=&amp;quot;text&amp;quot;&amp;gt;輸入時，每打一個字都要等1-2秒字元才會顯示&lt;/font&gt;&amp;quot;的問題。&lt;/p&gt;  &lt;p&gt;開啟VM使用IE8可重現問題；而同樣網頁在IE9下則運作正常。針對此一IE下的JavaScript茶包，決定交由&lt;a href="http://blog.darkthread.net/post-2009-06-08-ie8-devtool-profiler.aspx"&gt;JavaScript效能茶包一哥&lt;/a&gt;--IE Dev Tools Profiler工具進行獵殺!&lt;/p&gt;  &lt;p&gt;使用IE8開啟問題網頁，按下F12啟用IE Dev Tools，故意觸發blockUI遮蔽重現輸入回應緩慢問題，此時選取IE Dev Tools的Profiler頁籤，按下&amp;quot;Start profiling&amp;quot;，輸入幾個字元後，再停止側錄。&lt;/p&gt;  &lt;p&gt;結果顯示，簡單幾個按鍵動作觸發了六千多次的sortOrder、siblingCheck以及高達17萬次的Array.unshift，頗為異常；而未使用blockUI遮蔽時則無此狀況。其中sortOrder耗用了數秒執行時間，疑似導致效能不彰的元兇。&lt;/p&gt;  &lt;p&gt;使用IE9進行相同測試，得到相近結果: 一樣在幾秒內連續觸發成千上萬次的sortOrder、siblingCheck及Array.unshift，唯一不同的是IE9並未因此發生輸入回應遲緩的現象。我推估是同樣的指令在IE9環境中執行方式有異或是IE9大幅提升了JavaScript Engine的執行效能，因而執行次數相近，卻只耗用了386ms(如下圖)，未明顯影響效能。&lt;/p&gt;  &lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8718/640x480.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;為追查導致異常大量呼叫的來源，將Current view切換為Call tree(下圖藍框處)，可看出一連串的sortOrder呼叫源於jquery.blockUI.js第448行。&lt;/p&gt;  &lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8717/640x480.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;雙擊上圖橘框處，可直接帶至相對的程式碼位置，由邏輯推斷疑似呼叫$(e.target).parents(&amp;quot;div.&amp;quot; + opts.blockMsgClass)這段所引發。&lt;/p&gt;  &lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8719/640x480.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;懷疑是blockUI舊版Bug或blockUI與jQuery版本匹配問題，比對了jquery.js及jquery.blockUI.js版本，發現blockUI已是最新版，而jquery.js則還是1.4.4，版本偏舊。試著將jquery.js換成1.4.2或1.7都不會引發問題，因此推測屬版本匹配問題。決定將jquery.js換為1.7.1版並確認其他功能未受影響後，問題排除~&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8751" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Trouble-Shooting" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx" /><category term="IE" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/IE/default.aspx" /><category term="jQuery" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/jQuery/default.aspx" /></entry><entry><title>建立VS2010專案項目範本</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-17-vs2010-item-template.aspx" /><id>http://blog2.darkthread.net/post-2012-01-17-vs2010-item-template.aspx</id><published>2012-01-17T15:46:05Z</published><updated>2012-01-17T15:46:05Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;針對簡單的AJAX前後端整合程式範例，我很愛以下這種HTML + JavaScript + C#三合一寫法:&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; %&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;html&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;head runat=&lt;span class="str"&gt;&amp;quot;server&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;title&amp;gt;AJAX Example&amp;lt;/title&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        body,input { font-size: 9pt; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js&amp;#39;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;   &lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    $(&lt;span class="kwrd"&gt;function&lt;/span&gt;() {&lt;/pre&gt;

    &lt;pre class="alt"&gt;    });&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;當Server端的程式邏輯不多時，我會選擇用Inline Code ASP.NET網頁，Server端程式不另拆成Blah.aspx.cs，而直接以&amp;lt;script runat=&amp;quot;server&amp;quot;&amp;gt;嵌在HTML中，如此HTML+JavaScript+C#集中在同一個程式，閱讀方便；而要測試時，因jQuery等引用自CDN，直接將範例程式碼存成Boo.aspx就可當場執行，馬上看結果，比拆成aspx, aspx.cs, js來得更加省事不沾手。&lt;/p&gt;

&lt;p&gt;不過，每次建立範例時總有些固定程序:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;建立一個新的Web Form項目，取消&amp;quot;Place code in separate file&amp;quot;(將程式碼置於個別檔案中)選項。 &lt;/li&gt;

  &lt;li&gt;VS2010預設的Web Form樣版內含 
    &lt;br /&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;及&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;等XHTML宣告。 

    &lt;br /&gt;我習慣改成HTML5&lt;a href="http://www.w3schools.com/html5/tag_doctype.asp"&gt;寫法&lt;/a&gt;: 簡化為&amp;lt;!DOCTYPE html&amp;gt;及&amp;lt;html&amp;gt;。 &lt;/li&gt;

  &lt;li&gt;加上&amp;lt;script src=&amp;quot;…&amp;quot;&amp;gt;指向&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/03/22/where-is-jquery-vsdoc.aspx"&gt;Microsoft CDN&lt;/a&gt;，載入jQuery。 &lt;/li&gt;

  &lt;li&gt;加上void Page_Load(object sender, EventArgs e)的Server端C#程式片段 &lt;/li&gt;

  &lt;li&gt;加上$(function() { });的Client端JavaScript程式片段 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;由於這幾個步驟每次寫範例都要重新玩一次，便興起弄成項目範本的念頭。原以為要花點功夫搞懂自訂範本的細節，找到91的一篇&lt;a href="http://www.dotblogs.com.tw/hatelove/archive/2011/07/06/visual-studio-create-your-project-template.aspx"&gt;專案範本建立教學&lt;/a&gt;，才發現貼心的Visual Studio已內建&amp;quot;下一步下一步&amp;quot;匯出精靈，將現有專案或項目轉成範本的做法出奇簡單，真是佛心來著。91介紹的是專案範本，建立項目範本的範例就交給我囉!&lt;/p&gt;

&lt;p&gt;首先，在專案裡先做好一個如前述程式的空白Inline Code ASPX，接著選&amp;quot;File-&amp;gt;Export Template&amp;quot;(部分細節91的&lt;a href="http://www.dotblogs.com.tw/hatelove/archive/2011/07/06/visual-studio-create-your-project-template.aspx"&gt;文章&lt;/a&gt;已有說明，此處不再贅述)，接著選擇要出Item Template(項目範本):&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8735/640x480.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;選取要匯出成範本的檔案項目:&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8736/640x480.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;中間還有一個指定參照程式庫項目的步驟，本案例不需要，故直接跳過。接著為範本指定名稱，加上說明，再配個Icon讓範本看起來更專業一點。&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8737/640x480.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;完成後，新增項目時就可以看到剛才做好的範本囉!&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8738/640x480.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;試用了一下，覺得這個精靈產生的陽春範本有兩個地方可以再強化: 1) 預設會使用範本名稱當作檔案名(如上圖的HTML5 ASP.NET Example.aspx)，而我想改成MyLab .aspx 2) &amp;lt;title&amp;gt;是固定的，想將其預設為新增aspx的檔案名&lt;/p&gt;

&lt;p&gt;查了MSDN文件，找到客製這兩個需求的方法:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;匯出的範本其實是一個ZIP檔案，其中包含了範本檔案來源、vstemplate設定檔、圖示檔等內容，被放在使用者文件資料夾的Visual Studio 2010\Templates\ItemTemplates目錄下。(如下圖) 
    &lt;br /&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8739/original.aspx" alt="" /&gt;&amp;#160; &lt;br /&gt;*.vstemplate是一個XML設定檔案，透過修改&amp;lt;DefaultName&amp;gt;即可自訂預設檔名: 

    &lt;div class="BlogCodeBlock"&gt;
      &lt;div class="csharpcode"&gt;
        &lt;pre class="alt"&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;TemplateData&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
      &lt;/div&gt;

      &lt;div class="csharpcode"&gt;
        &lt;pre&gt;    &lt;font style="background-color:#ffff00;"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;DefaultName&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;MyLab.aspx&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;DefaultName&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/font&gt;&lt;/pre&gt;
      &lt;/div&gt;

      &lt;div class="csharpcode"&gt;
        &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;HTML5 ASP.NET Example&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;br /&gt;&lt;/li&gt;

  &lt;li&gt;Visual Studio提供了一些&lt;a href="http://msdn.microsoft.com/en-us/library/eehb4faa(VS.80).aspx"&gt;預設範本參數&lt;/a&gt;，允許我們在範本中套用專案名稱、使用者名稱、機器名稱、項目名稱、根命名空間等資訊。因此只要在Lab1.aspx中將&amp;lt;title&amp;gt;內容換成$itemname$，在產生新檔案時，就會被自動換成檔案名稱。 

    &lt;div class="BlogCodeBlock"&gt;
      &lt;div class="csharpcode"&gt;
        &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;font style="background-color:#ffff00;"&gt;$itemname$&lt;/font&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;text/javascript&amp;#39;&lt;/span&gt; &lt;/pre&gt;

        &lt;pre&gt;            &lt;span class="attr"&gt;src&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js&amp;#39;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;   &lt;/pre&gt;

        &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;解壓縮取出ZIP檔中的MyTemplate.vstemplate及Lab1.aspx，修改後再重新壓縮，能節省繁瑣手工的範本就完成囉! Visual Studio Rocks!!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8740/original.aspx" alt="" /&gt;&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8748" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Visual Studio" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Visual+Studio/default.aspx" /></entry><entry><title>筆記-IIS 7網站目錄的最小NTFS權限</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-17-minimum-ntfs-permission-in-iis-7.aspx" /><id>http://blog2.darkthread.net/post-2012-01-17-minimum-ntfs-permission-in-iis-7.aspx</id><published>2012-01-17T09:43:49Z</published><updated>2012-01-17T09:43:49Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;IIS 7新增了虛擬帳戶(Virtual Accounts)的概念，即每個應用程式集區(App Pool)都有一個專屬的IIS APPPOOL\AppPoolName虛擬帳號，便於更精準地控管網站相關檔案及其他資源的存取權限。(關於虛擬帳號概念，可參考保哥的詳細介紹: &lt;a href="http://blog.miniasp.com/post/2009/09/09/Introduce-IIS-75-Application-Pool-Identity-and-Virtual-Account.aspx"&gt;介紹 IIS 7.5 的應用程式集區與新增的「虛擬帳戶」特性&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;權限管控的最高指導原則在力求&amp;quot;在維持可運作的前題下，開放最少權限&amp;quot;，因此拔除網站目錄的預設NTFS權限理，只授權給最少的必要帳號，將能提高安全性。不過，IIS 7&amp;quot;最小權限&amp;quot;的範圍為何?&lt;/p&gt;  &lt;p&gt;找了一些探討IIS權限文章，沒找到關於NTFS權限需求較直接明確的說明，最後只依理論跟實驗結果整理出一點心得。(如果有人看過明確的文件說明，歡迎分享)&lt;/p&gt;  &lt;p&gt;我用以下程式偵測ASP.NET執行身分的相關資訊:&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%@ Page Language=&amp;quot;C#&amp;quot; %&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Response.Write(&lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;Request.IsAuthenticated=&amp;quot;&lt;/span&gt; + &lt;/pre&gt;

    &lt;pre&gt;            Request.IsAuthenticated);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Response.Write(&lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;Request.LogonUserIdentity.Name=&amp;quot;&lt;/span&gt; + &lt;/pre&gt;

    &lt;pre&gt;            Request.LogonUserIdentity.Name);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        Response.Write(&lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;WindowsIdentity.GetCurrent().Name=&amp;quot;&lt;/span&gt; +&lt;/pre&gt;

    &lt;pre&gt;            System.Security.Principal.WindowsIdentity.GetCurrent().Name);&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;在匿名存取時結果如下:&lt;/p&gt;

&lt;li&gt;Request.IsAuthenticated=False &lt;/li&gt;

&lt;li&gt;Request.LogonUserIdentity.Name=NT AUTHORITY\IUSR &lt;/li&gt;

&lt;li&gt;WindowsIdentity.GetCurrent().Name=IIS APPPOOL\JEFFPool &lt;/li&gt;

&lt;p&gt;啟用Windows Authentication時則為:&lt;/p&gt;

&lt;li&gt;Request.IsAuthenticated=True &lt;/li&gt;

&lt;li&gt;Request.LogonUserIdentity.Name=MyDoamin\MyAccount &lt;/li&gt;

&lt;li&gt;WindowsIdentity.GetCurrent().Name=IIS APPPOOL\JEFFPool &lt;/li&gt;

&lt;p&gt;要透過IIS執行程式，帳號必須對網站資料夾至少有讀取與執行(Read &amp;amp; execute)、列出資料夾內容(List folder contents)等權限。原本以為只需要開放給IIS APPPOOL\JEFFPool帳號就好，但實測發現在上述兩種情況下，還分別需要授與權限給NT AUTHORITY\IUSR及MyDoamin\MyAccount，不然就會產生拒絕存取錯誤。&lt;/p&gt;

&lt;p&gt;透過Process Monitor我觀察到w3wp.exe確實是以IIS APPPOOL\JEFFPool身分執行，但在執行showid.aspx時偷偷調閱了aspx的檔案權限設定，推測(純屬推測)其目的是用來比對Request.LogonUserIdentity的使用者在showid.aspx的NTFS權限清單中是否具有讀取執行權。因此Request.LogonUserIdentity跟IIS APPPOOL\JEFFPool兩個帳號都要有權限才不會產生存取錯誤。&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8744/original.aspx" alt="" /&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8745/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;最後我整理的結論為:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;font color="#f79646"&gt;匿名存取(或表單驗證, Form Authentcation)時，網站資料夾的最少授權對象為NT AUTHORITY\IUSR及IIS APPPOOL\YourPoolName；&lt;/font&gt; &lt;/li&gt;

  &lt;li&gt;&lt;font color="#f79646"&gt;Windows驗證時，除IIS APPPOOL\YourPoolName外，要再指定為Authenticated Users群組、Domain Users群組，或是可登入使用者所屬的特定群組三者之一。&lt;/font&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PS: 當啟用&lt;a href="http://msdn.microsoft.com/zh-tw/library/aa292118(VS.71).aspx"&gt;ASP.NET模擬&lt;/a&gt;(Impersonation)時，會影響ASP.NET的執行身份(可由WindowsIdentity.GetCurrent()觀察)，授權對象需再自行調整。&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8746" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="ASP.NET" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx" /><category term="IIS" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/IIS/default.aspx" /></entry><entry><title>【茶包射手日記】Word文件中的超連結變成{ HYPERLINK ... }</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-13-word-hyperlink-markup.aspx" /><id>http://blog2.darkthread.net/post-2012-01-13-word-hyperlink-markup.aspx</id><published>2012-01-13T04:40:04Z</published><updated>2012-01-13T04:40:04Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;接獲同事求助: Word 2010無法正確顯示內嵌超連結的文件。&lt;/p&gt;  &lt;p&gt;文件的正常顯示應如下:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8732/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;但同事使用Word 2010開啟同該文件時，超連結的部分會變成{ HYPERLINK &amp;quot;...&amp;quot; }。&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8733/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;過去小玩過Word VBA，憑印象中認出&amp;quot;功能變數&amp;quot;的標註格式，故懷疑是同事誤觸&amp;quot;直接顯示功能變數標記&amp;quot;之類的選項造成的。花了點功夫才找到設定位置(如下圖)，原來選項的正確名稱是&amp;quot;顯示功能變數代碼代替數值&amp;quot;:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8734/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;關閉上述選項後，文件的顯示便恢復正常，&lt;strike&gt;經偵訊同事也對亂調選項一事坦承不諱&lt;/strike&gt;經訊問後確認同事曾試調過此部分的設定，可能當時動到而不自知。&lt;/p&gt;  &lt;p&gt;本案順利終結!&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8741" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Trouble-Shooting" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx" /></entry><entry><title>【茶包射手日記】RDLC報表出現記憶體不足錯誤</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2012-01-12-rdlc-out-of-memory.aspx" /><id>http://blog2.darkthread.net/post-2012-01-12-rdlc-out-of-memory.aspx</id><published>2012-01-12T00:30:00Z</published><updated>2012-01-12T00:30:00Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;&lt;a href="http://blog.darkthread.net/post-2009-01-31-reportviewer-rdlc.aspx"&gt;RDLC&lt;/a&gt;比Reporting Service靈活彈性，不需要依賴額外的伺服器，評估起來是很優秀的報表解決方案。&lt;/p&gt;  &lt;p&gt;很放心地鼔吹將專案中的報表逐一汰換成RDLC，直到&lt;strike&gt;我膝蓋中了一箭&lt;/strike&gt;最近踩到一顆地雷.. &lt;/p&gt;  &lt;p&gt;專案有個&lt;a href="http://blog.darkthread.net/post-2009-01-31-reportviewer-rdlc.aspx"&gt;RDLC&lt;/a&gt;查詢報表，大約近萬筆資料，每筆約20個欄位，資料量稍多但還不致令人髮指，執行時卻常常發生記憶體不足(Out of Memory)錯誤! (有時是第一次執行OK，第二次就出現記憶體不足)&lt;/p&gt;  &lt;p&gt;檢視報表設計，發現其中用了很多Expression寫法處理資料格式轉換，例如: [=Fields!ColumnA.Value &amp;amp; vbCrLf &amp;amp; Fields!ColumnB.Value]、[=Format(Fields!MyDate.Value, &amp;quot;MMMM&amp;quot;)]…。直覺上這些額外運算遍佈在每一筆資料的各欄位中，一旦資料筆數眾多就會被反覆執行，預期會消耗大量記憶體及CPU，懷疑有可能就是導致記憶體不足的元凶。&lt;/p&gt;  &lt;p&gt;查了資料，在Microsoft Connect找到兩篇相關回報，證實了此一假設:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/527451/ms-report-viewer-memory-leak-any-update-fix-winforms-application"&gt;MS Report Viewer Memory Leak - Any Update / Fix? (WinForms Application)&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/666364/report-viewer-2010-sp1-memory-leak"&gt;Report Viewer 2010 SP1 Memory Leak&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;其中網友MikeGCompass明確地重現了問題:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;I have 2 very simple reports one with an expression and another almost identical but it doesn&amp;#39;t contain any report expressions.        &lt;br /&gt;ReportBad.rdlc has 1 expression and everytime you generate the PDF it grows in memory, so after a few executes you can have my simple EXE holding on to 1GB or more of memory.         &lt;br /&gt;ReportGood.rdlc has 0 expressions and everytime you generate it does not grow in memory.         &lt;br /&gt;Thus I conclude using expression columns causes some resources never to be released, even when you dispose the report viewer control.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;而在我們的案例中，問題報表在移除Expression後，記憶體不足的狀況確實也消失了。&lt;/p&gt;  &lt;p&gt;依此看來，RDLC存在著因Expression導致Memory Leak的Bug，且到VS2010的版本為止尚未解決。目前較可行的解決之道，是在RDLC中避用Expression寫法，例如: 在SQL查詢語法中就預先做轉換，或是先將資料匯入DataTable再進行&amp;quot;後製&amp;quot;，會造成些許不便，但尚在可接受範圍之內，在Bug被修復前，就採取&amp;quot;&lt;font color="#f79646"&gt;RDLC中儘可能避免使用Expression運算式&lt;/font&gt;&amp;quot;的策略避避風頭吧!&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8731" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Reporting Service" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Reporting+Service/default.aspx" /><category term="Trouble-Shooting" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx" /></entry><entry><title>AJAX式資料清單的新選擇-Kendo UI Grid</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-31-kendo-ui-grid.aspx" /><id>http://blog2.darkthread.net/post-2011-12-31-kendo-ui-grid.aspx</id><published>2011-12-31T10:23:00Z</published><updated>2011-12-31T10:23:00Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;這幾年在專案中，針對表格式資料的呈現，我多已摒棄PostBack寫法，改用AJAX動態提取方式處理換頁、排列、重新查詢等資料查詢需求，如此可避免傳統PostBack時畫面會閃一下的缺點，提供使用者較流暢的操作感受。&lt;/p&gt;  &lt;p&gt;前陣子寫過一篇筆記: &lt;a href="http://blog.darkthread.net/post-2011-12-01-telerik-radgrid-ajax-binding.aspx"&gt;Telerik RadGrid AJAX更新範例&lt;/a&gt;，介紹的便是應用Telerik的RadControls for ASP.NET AJAX元件庫，實現純AJAX式資料來源的GridView。不過，該元件基於ASP.NET AJAX Client Library並非jQuery，加上元件為ASP.NET控制項，不能安插於純HTML網頁中，限制較多；之前還介紹過另一套&lt;a href="http://blog.darkthread.net/post-2011-06-05-telerik-ext-for-asp-net-mvc.aspx"&gt;Telerik Extensions for ASP.NET MVC&lt;/a&gt;，是以jQuery為基礎打造的Client-Side Library，但它主要設計在ASP.NET MVC cshtml中被呼叫，若要在非ASP.NET MVC環境中使用，需要花功夫自己包Plug-In。(是的，這事兒我也幹了...)&lt;/p&gt;  &lt;p&gt;本月初(2011/12)，Telerik推出了&lt;a href="http://www.kendoui.com/"&gt;Kendo UI&lt;/a&gt; Framework，一套以HTML5 + jQuery打造的精緻UI元件組，依循如Telerik RadControls for ASP.NET的傳統，照例網羅了日期選擇器、頁籤、選單、Grid、TreeView... 等網頁開發會用到的大小控制項，一方面善用HTML5 + CSS3的威力，另一方面繼續保持跨平台相容(跨IE, Firefox, Chrome, Safari, Opera等瀏覽器, 但HTML5支援度較差的IE7/IE8也OK，還支援WP7, iOS及Android等行動裝置)，看起來是純Client-Side元件的一項不錯選擇。&lt;/p&gt;  &lt;p&gt;在授權上，Kendo UI跟Extensions for ASP.NET MVC一樣採雙授權方式: 商業版本可享有技術支援，每月更新，並可包在商品中發行販售；GPL授權則提供每季更新的開放源碼，若只應用在自己開發的網站中，不會再將網站程式本體作為商品販售散佈，理論上均可自由取得及修改，即使網路對外營運也在合法授權範圍內。(網站引用GPL元件授權議題可參見上回&lt;a href="http://blog.darkthread.net/post-2011-06-05-telerik-ext-for-asp-net-mvc.aspx#7908"&gt;討論&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;從此，要在網站專案中實現AJAX式的GridView，又多了一項選擇。這裡就依著上回&lt;a href="http://blog.darkthread.net/post-2011-12-01-telerik-radgrid-ajax-binding.aspx"&gt;Telerik RadGrid AJAX更新範例&lt;/a&gt;的規格，改用Kendo UI Grid來實做驗證一番! 如下圖，分頁、排序、關鍵字查詢功能都不是問題，看來足以滿足專案的基本需求。&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8708/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;Grid1.htm程式碼如下: &lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt; &lt;span class="attr"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Grid Lab 1&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;&amp;lt;!--In the header of your page, paste the following for Kendo styles--&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;link&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;../styles/kendo.common.min.css&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;rel&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;link&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;../styles/kendo.default.min.css&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;rel&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;&amp;lt;!--Then paste the following for Kendo scripts--&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;src&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;../js/jquery.min.js&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;quot;../js/kendo.all.min.js&amp;quot;&lt;/span&gt; type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        body { font-size: 9pt; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        #dvGrid { width: 500px; }&lt;/pre&gt;

    &lt;pre&gt;        span.hi-lite { color: red; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        #dvGrid th.k-header { text-align: center }&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//建立資料來源物件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; dataSrc = &lt;span class="kwrd"&gt;new&lt;/span&gt; kendo.data.DataSource({&lt;/pre&gt;

    &lt;pre class="alt"&gt;                transport: {&lt;/pre&gt;

    &lt;pre&gt;                    read: {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//以下其實就是$.ajax的參數&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                        type: &lt;span class="str"&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        url: &lt;span class="str"&gt;&amp;quot;JsonDataSrc.ashx&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;                        dataType: &lt;span class="str"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        data: {&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="rem"&gt;//額外傳至後方的參數&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            keywd: &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                                &lt;span class="kwrd"&gt;return&lt;/span&gt; $(&lt;span class="str"&gt;&amp;quot;#tKeyword&amp;quot;&lt;/span&gt;).val();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            }&lt;/pre&gt;

    &lt;pre&gt;                        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                },&lt;/pre&gt;

    &lt;pre class="alt"&gt;                schema: {&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//取出資料陣列&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    data: &lt;span class="kwrd"&gt;function&lt;/span&gt; (d) { &lt;span class="kwrd"&gt;return&lt;/span&gt; d.Data; },&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//取出資料總筆數(計算頁數用)&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    total: &lt;span class="kwrd"&gt;function&lt;/span&gt; (d) { &lt;span class="kwrd"&gt;return&lt;/span&gt; d.TotalCount; }&lt;/pre&gt;

    &lt;pre&gt;                },&lt;/pre&gt;

    &lt;pre class="alt"&gt;                pageSize: 10,&lt;/pre&gt;

    &lt;pre&gt;                serverPaging: &lt;span class="kwrd"&gt;true&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                serverSorting: &lt;span class="kwrd"&gt;true&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//JSON日期轉換&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; dateRegExp = /^\/Date\((.*?)\)\/$/;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            window.toDate = &lt;span class="kwrd"&gt;function&lt;/span&gt; (value) {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; date = dateRegExp.exec(value);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Date(parseInt(date[1]));&lt;/pre&gt;

    &lt;pre&gt;            }&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;            $(&lt;span class="str"&gt;&amp;quot;#dvGrid&amp;quot;&lt;/span&gt;).kendoGrid({&lt;/pre&gt;

    &lt;pre class="alt"&gt;                dataSource: dataSrc,&lt;/pre&gt;

    &lt;pre&gt;                columns: [&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    { field: &lt;span class="str"&gt;&amp;quot;UserNo&amp;quot;&lt;/span&gt;, title: &lt;span class="str"&gt;&amp;quot;會員編號&amp;quot;&lt;/span&gt; },&lt;/pre&gt;

    &lt;pre&gt;                    { field: &lt;span class="str"&gt;&amp;quot;UserName&amp;quot;&lt;/span&gt;, title: &lt;span class="str"&gt;&amp;quot;會員名稱&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;template: &lt;span class="str"&gt;&amp;#39;#= &amp;quot;&amp;lt;span class=\\&amp;quot;u-name\\&amp;quot;&amp;gt;&amp;quot; + UserName + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot; #&amp;#39;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    },&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    { field: &lt;span class="str"&gt;&amp;quot;RegDate&amp;quot;&lt;/span&gt;, title: &lt;span class="str"&gt;&amp;quot;加入日期&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;template: &lt;span class="str"&gt;&amp;#39;#= kendo.toString(toDate(RegDate), &amp;quot;yyyy/MM/dd&amp;quot;)#&amp;#39;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    },&lt;/pre&gt;

    &lt;pre&gt;                    { field: &lt;span class="str"&gt;&amp;quot;Points&amp;quot;&lt;/span&gt;, title: &lt;span class="str"&gt;&amp;quot;累積點數&amp;quot;&lt;/span&gt; },&lt;/pre&gt;

    &lt;pre class="alt"&gt;                ],&lt;/pre&gt;

    &lt;pre&gt;                sortable: &lt;span class="kwrd"&gt;true&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                pageable: &lt;span class="kwrd"&gt;true&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;                dataBound: &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//AJAX資料Bind完成後觸發&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;var&lt;/span&gt; kw = $(&lt;span class="str"&gt;&amp;quot;#tKeyword&amp;quot;&lt;/span&gt;).val();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//若有設關鍵字，做Highlight處理&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (kw.length &amp;gt; 0) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;var&lt;/span&gt; re = &lt;span class="kwrd"&gt;new&lt;/span&gt; RegExp(kw, &lt;span class="str"&gt;&amp;quot;g&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                        $(&lt;span class="str"&gt;&amp;quot;.u-name&amp;quot;&lt;/span&gt;).each(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;var&lt;/span&gt; $td = $(&lt;span class="kwrd"&gt;this&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                            $td.html($td.text()&lt;/pre&gt;

    &lt;pre class="alt"&gt;                           .replace(re, &lt;span class="str"&gt;&amp;quot;&amp;lt;span class=&amp;#39;hi-lite&amp;#39;&amp;gt;$&amp;amp;&amp;lt;/span&amp;gt;&amp;quot;&lt;/span&gt;));&lt;/pre&gt;

    &lt;pre&gt;                        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                }&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//按下查詢鈕&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#bQuery&amp;quot;&lt;/span&gt;).click(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="rem"&gt;//要求資料來源重新讀取(並指定切至第一頁)&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                dataSrc.read({ page: 1, skip: 0 });&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="rem"&gt;//Grid重新顯示資料&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                $(&lt;span class="str"&gt;&amp;quot;#dvGrid&amp;quot;&lt;/span&gt;).data(&lt;span class="str"&gt;&amp;quot;kendoGrid&amp;quot;&lt;/span&gt;).refresh();&lt;/pre&gt;

    &lt;pre&gt;            });&lt;/pre&gt;

    &lt;pre class="alt"&gt;        });&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;padding: 10px;&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    關鍵字: &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;tKeyword&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;button&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;查詢&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;bQuery&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;dvGrid&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;後端我寫了一個JsonDataSrc.ashx來負責資料供給，其中程式邏輯與上回RadGrid版本幾乎完全相同。(只要能正確傳回JSON即可，要改寫成PHP、Ruby應該也不會是難事)&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&amp;lt;%@ WebHandler Language=&lt;span class="str"&gt;&amp;quot;C#&amp;quot;&lt;/span&gt; Class=&lt;span class="str"&gt;&amp;quot;JsonDataSrc&amp;quot;&lt;/span&gt; %&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web.Script.Serialization;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Drawing;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Reflection;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; JsonDataSrc : IHttpHandler {&lt;/pre&gt;

    &lt;pre&gt;    &lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//模擬資料物件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SimMemberInfo&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; UserNo; &lt;span class="rem"&gt;//會員編號&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; UserName; &lt;span class="rem"&gt;//會員名稱&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; DateTime RegDate; &lt;span class="rem"&gt;//註冊日期&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Points; &lt;span class="rem"&gt;//累積點數&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; List&amp;lt;SimMemberInfo&amp;gt; _SimuDataStore = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;//結果物件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ResultData &lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Data;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; TotalCount;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;    &lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ProcessRequest (HttpContext context) {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_SimuDataStore == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            Random rnd = &lt;span class="kwrd"&gt;new&lt;/span&gt; Random();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//借用具名顏色名稱來產生隨機資料&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt;[] colorNames = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Color)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                .GetProperties(BindingFlags.Static | BindingFlags.Public)&lt;/pre&gt;

    &lt;pre&gt;                .Select(o =&amp;gt; o.Name).ToArray();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            _SimuDataStore =&lt;/pre&gt;

    &lt;pre&gt;                colorNames&lt;/pre&gt;

    &lt;pre class="alt"&gt;                .Select(cn =&amp;gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; SimMemberInfo()&lt;/pre&gt;

    &lt;pre&gt;                {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    UserNo = &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;C{0:00000}&amp;quot;&lt;/span&gt;, rnd.Next(99999)),&lt;/pre&gt;

    &lt;pre&gt;                    UserName = cn,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    RegDate = DateTime.Today.AddDays(-rnd.Next(1000)),&lt;/pre&gt;

    &lt;pre&gt;                    Points = rnd.Next(9999)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                }).ToList();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; keywd = context.Request[&lt;span class="str"&gt;&amp;quot;keywd&amp;quot;&lt;/span&gt;];&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; sortField = context.Request[&lt;span class="str"&gt;&amp;quot;sort[0][field]&amp;quot;&lt;/span&gt;];&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; sortDir = context.Request[&lt;span class="str"&gt;&amp;quot;sort[0][dir]&amp;quot;&lt;/span&gt;];&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//指定關鍵字時，使用Contains()對UserName進行比對&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        var res = _SimuDataStore.Where(o =&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(keywd) || o.UserName.Contains(keywd));&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(sortField))&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//宣告一個函數可傳回SimMemberInfo之指定屬性值用於依動態欄位排序&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Func&amp;lt;SimMemberInfo, &lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; GetColString =&lt;/pre&gt;

    &lt;pre&gt;                (o, c) =&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                {&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;switch&lt;/span&gt; (c)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    {&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="kwrd"&gt;case&lt;/span&gt; &lt;span class="str"&gt;&amp;quot;UserNo&amp;quot;&lt;/span&gt;: &lt;span class="kwrd"&gt;return&lt;/span&gt; o.UserNo;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;case&lt;/span&gt; &lt;span class="str"&gt;&amp;quot;UserName&amp;quot;&lt;/span&gt;: &lt;span class="kwrd"&gt;return&lt;/span&gt; o.UserName;&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="kwrd"&gt;case&lt;/span&gt; &lt;span class="str"&gt;&amp;quot;RegDate&amp;quot;&lt;/span&gt;: &lt;span class="kwrd"&gt;return&lt;/span&gt; o.RegDate.ToString(&lt;span class="str"&gt;&amp;quot;yyyyMMdd&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;case&lt;/span&gt; &lt;span class="str"&gt;&amp;quot;Points&amp;quot;&lt;/span&gt;: &lt;span class="kwrd"&gt;return&lt;/span&gt; o.Points.ToString();&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="kwrd"&gt;default&lt;/span&gt;: &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentException();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                };&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (sortDir == &lt;span class="str"&gt;&amp;quot;asc&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre&gt;                res = res.OrderBy(o =&amp;gt; GetColString(o, sortField));&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                res = res.OrderByDescending(o =&amp;gt; GetColString(o, sortField));&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }        &lt;/pre&gt;

    &lt;pre&gt;        &lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;/pre&gt;

    &lt;pre&gt;        JavaScriptSerializer jss = &lt;span class="kwrd"&gt;new&lt;/span&gt; JavaScriptSerializer();&lt;/pre&gt;

    &lt;pre class="alt"&gt;        context.Response.ContentType = &lt;span class="str"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; pageSize = 10, take = 10, skip = 0;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt;.TryParse(context.Request[&lt;span class="str"&gt;&amp;quot;pageSize&amp;quot;&lt;/span&gt;], &lt;span class="kwrd"&gt;out&lt;/span&gt; pageSize);&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt;.TryParse(context.Request[&lt;span class="str"&gt;&amp;quot;take&amp;quot;&lt;/span&gt;], &lt;span class="kwrd"&gt;out&lt;/span&gt; take);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt;.TryParse(context.Request[&lt;span class="str"&gt;&amp;quot;skip&amp;quot;&lt;/span&gt;], &lt;span class="kwrd"&gt;out&lt;/span&gt; skip);&lt;/pre&gt;

    &lt;pre&gt;        var paged = res.Skip(skip).Take(take);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        context.Response.Write(jss.Serialize(&lt;span class="kwrd"&gt;new&lt;/span&gt; ResultData()&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Data = paged.ToList(),&lt;/pre&gt;

    &lt;pre&gt;            TotalCount = res.Count()&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }));&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt; &lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsReusable {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        get {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;想馬上實際動手玩的朋友可以試試上述範例程式的&lt;a href="http://www.darkthread.net/aspnet4/kendoui/grid/grid1.htm"&gt;線上版&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;寫完2011年最後一篇文囉! 準備迎接2012，祝大家新年快樂~~&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8709" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Kendo UI" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Kendo+UI/default.aspx" /></entry><entry><title>Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-25-live-sdk-notes-5.aspx" /><id>http://blog2.darkthread.net/post-2011-12-25-live-sdk-notes-5.aspx</id><published>2011-12-25T01:36:13Z</published><updated>2011-12-25T01:36:13Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;一連寫了四篇筆記，介紹了Live SDK，也實際玩過取得使用者Live ID身分、連上SkyDrive等把戲:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://blog.darkthread.net/post-2011-12-12-live-sdk-notes-1.aspx"&gt;Live SDK筆記1-簡介&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;Live SDK筆記2-註冊App與基本術語&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blog.darkthread.net/post-2011-12-15-live-sdk-notes-3.aspx"&gt;Live SDK筆記3-使用Live ID登入ASP.NET網站&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blog.darkthread.net/post-2011-12-16-live-sdk-notes-4.aspx"&gt;Live SDK筆記4-SkyDrive Explorer, ASP.NET版&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;埋了這些哽，其實背後我最想做的，是從WP7程式連上SkyDrive! &lt;/p&gt;  &lt;p&gt;手機基於儲存空間的限制，以及跨裝置/平台共享的需求，非常需要結合網路儲存空間做為後盾，而WP7配上微軟體系的SkyDrive，感覺上是很棒的組合。&lt;/p&gt;  &lt;p&gt;Live SDK有提供WP7專用的元件庫，下載安裝Live SDK後，可在WP7專案參照中找到Microsoft.Live及Microsoft.Live.Controls，學習中心有篇WP7整合Live SDK&lt;a href="http://msdn.microsoft.com/zh-tw/windowslive/hh561314"&gt;教學&lt;/a&gt;，是很好的入門。&lt;/p&gt;  &lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8685/640x480.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;Live SDK提供SignInButton控制項，表面上是顆登入登出鈕，背地則幫我們處理掉在App內嵌瀏覽器載入Live帳號登入網頁以及同意網頁等繁瑣細節，是整合Live SDK最簡便的做法。如果在Visual Studio工具箱沒看到它，可自行找到Microsoft.Live.Controls.dll加入工具箱(如下圖)。&lt;/p&gt;  &lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8686/640x480.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;不過，有一點要注意，由於在App運作時，不像在&lt;a href="http://blog.darkthread.net/post-2011-12-15-live-sdk-notes-3.aspx"&gt;ASP.NET範例&lt;/a&gt;中會有Callback.aspx負責接入Authorization Code再呼叫API取得Access Token。請參考&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;註冊App&lt;/a&gt;一文第3點提及的做法，申請Client ID時，Redirect Domain請留白，然後勾選&amp;quot;Mobile client app&amp;quot;為&amp;quot;Yes&amp;quot;，如此，此Client ID即可使用httqs://oauth.live.com/desktop做為Redirect Page，並直接取得Access Token，如此在行動裝置上即可獨立運作。&lt;/p&gt;  &lt;p&gt;我想試做的玩具是一個SkyDrive簡易瀏覽器，功能跟&lt;a href="http://blog.darkthread.net/post-2011-12-16-live-sdk-notes-4.aspx"&gt;上一篇&lt;/a&gt;的ASP.NET版差不多，取得使用者對wl.skydrive範圍的同意權後，列出使用者SkyDrive下的目錄，點選目錄可展開列出其下的子項目，若是檔案則可進行檢視，不過WP7能支援的檔案格式比PC少，我只先選擇對圖片(jpg, png)及影片(wmv, mp3, mp4)提供檢視功能。&lt;/p&gt;  &lt;p&gt;MainPage.xaml放入一個SignInButton及ListBox:&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;LayoutRoot&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Background&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Transparent&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid.RowDefinitions&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;RowDefinition&lt;/span&gt; &lt;span class="attr"&gt;Height&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Auto&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;RowDefinition&lt;/span&gt; &lt;span class="attr"&gt;Height&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid.RowDefinitions&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;StackPanel&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;TitlePanel&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;12,17,0,28&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;TextBlock&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ApplicationTitle&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Mini SkyDrive Explorer&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre&gt;                       &lt;span class="attr"&gt;Style&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{StaticResource PhoneTextNormalStyle}&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;my:SignInButton&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnSignIn&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Branding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Skydrive&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre&gt;             &lt;span class="attr"&gt;SessionChanged&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnSignIn_SessionChanged&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;CientId&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0000000016888888&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre class="alt"&gt;             &lt;span class="attr"&gt;RedirectUri&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;https://oauth.live.com/desktop&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre&gt;             &lt;span class="attr"&gt;Scopes&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;wl.signin wl.basic wl.skydrive&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;StackPanel&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ContentPanel&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;12,0,12,0&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;ListBox&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;DirList&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;FontSize&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;36&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre&gt;                     &lt;span class="attr"&gt;MouseLeftButtonUp&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;DirList_MouseLeftButtonUp&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;ListBox&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;MainPage.xaml.cs則加入程式邏輯，主要原理是在使用者完成登入及同意後(btnSignIn_SessionChanged事件)建立一個LiveConnectClient，即可使用它呼叫REST API執行各種操作。呼叫結果由client_GetCompleted事件解析，相關的註解說明我附加在程式裡，所以直接看Code:&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Input;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Controls;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Live;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Live.Controls;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MiniSkyDriveExplorer&lt;/pre&gt;

    &lt;pre class="alt"&gt;{&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MainPage : PhoneApplicationPage&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;// Constructor&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; MainPage()&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            InitializeComponent();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; LiveConnectClient client = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; btnSignIn_SessionChanged(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;/pre&gt;

    &lt;pre&gt;            LiveConnectSessionChangedEventArgs e)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (e.Status == LiveConnectSessionStatus.Connected)&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                DirItem.LiveSession = e.Session;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                client = &lt;span class="kwrd"&gt;new&lt;/span&gt; LiveConnectClient(e.Session);&lt;/pre&gt;

    &lt;pre&gt;                client.GetCompleted += &lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;LiveOperationCompletedEventArgs&amp;gt;(&lt;/pre&gt;

    &lt;pre&gt;                        client_GetCompleted);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//呼叫me/skydrive，取得根目錄folderId&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                client.GetAsync(&lt;span class="str"&gt;&amp;quot;me/skydrive&amp;quot;&lt;/span&gt;, &lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;new&lt;/span&gt; MyUserState(ApiMethod.SkyDriveProp));&lt;/pre&gt;

    &lt;pre&gt;            }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; goUpperSymbol = &lt;span class="str"&gt;&amp;quot;\t&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//所有的REST API呼叫動作完成後，都會觸發此段解析回傳結果&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//為降低範例程式複雜度，以下並未包含防呆容錯的邏輯，實際開發時應補上&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;void&lt;/span&gt; client_GetCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;/pre&gt;

    &lt;pre&gt;                                 sender, LiveOperationCompletedEventArgs e)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            MyUserState state = e.UserState &lt;span class="kwrd"&gt;as&lt;/span&gt; MyUserState;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (state == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;switch&lt;/span&gt; (state.Method)&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="rem"&gt;//取得SkyDrive主資料夾的folderId&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; ApiMethod.SkyDriveProp:&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//取出id，列出根目錄下的項目&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    ListFiles(e.Result[&lt;span class="str"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;].ToString());&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//取得目錄清單&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; ApiMethod.SkyDriveDir:&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//由data取得陣列&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    List&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; items =&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        e.Result[&lt;span class="str"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;] &lt;span class="kwrd"&gt;as&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;;&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (items != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    {&lt;/pre&gt;

    &lt;pre&gt;                        DirList.Items.Clear();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        DirList.DisplayMemberPath = &lt;span class="str"&gt;&amp;quot;DisplayName&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="rem"&gt;//加入回上層的邏輯&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (folderIdStack.Count &amp;gt; 1)&lt;/pre&gt;

    &lt;pre&gt;                        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            DirItem di = &lt;span class="kwrd"&gt;new&lt;/span&gt; DirItem(goUpperSymbol, &lt;span class="str"&gt;&amp;quot;[..]&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                            DirList.Items.Add(di);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        }&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt; item &lt;span class="kwrd"&gt;in&lt;/span&gt; items)&lt;/pre&gt;

    &lt;pre&gt;                        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            DirItem di  = &lt;span class="kwrd"&gt;new&lt;/span&gt; DirItem(&lt;/pre&gt;

    &lt;pre&gt;                                item[&lt;span class="str"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;].ToString(),&lt;/pre&gt;

    &lt;pre class="alt"&gt;                                item[&lt;span class="str"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;].ToString()&lt;/pre&gt;

    &lt;pre&gt;                            );&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="rem"&gt;//資料夾時額外取得子項目數&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="kwrd"&gt;if&lt;/span&gt; (di.IsFolder)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                                di.Count = &lt;span class="kwrd"&gt;int&lt;/span&gt;.Parse(item[&lt;span class="str"&gt;&amp;quot;count&amp;quot;&lt;/span&gt;].ToString());&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="rem"&gt;//檔案則取得下載網址&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                                di.SrcUrl = item[&lt;span class="str"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;].ToString();&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="rem"&gt;//加入清單&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            DirList.Items.Add(di);&lt;/pre&gt;

    &lt;pre&gt;                        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//用以保存上層目錄的folderId&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; Stack&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; folderIdStack = &lt;span class="kwrd"&gt;new&lt;/span&gt; Stack&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ListFiles(&lt;span class="kwrd"&gt;string&lt;/span&gt; folderId)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (client == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (folderId == goUpperSymbol)&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                folderIdStack.Pop();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                folderId = folderIdStack.Peek();&lt;/pre&gt;

    &lt;pre&gt;            }&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; folderIdStack.Push(folderId);&lt;/pre&gt;

    &lt;pre&gt;            client.GetAsync(folderId + &lt;span class="str"&gt;&amp;quot;/files&amp;quot;&lt;/span&gt;, &lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;new&lt;/span&gt; MyUserState(ApiMethod.SkyDriveDir));&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; DirList_MouseLeftButtonUp(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;/pre&gt;

    &lt;pre&gt;                                               MouseButtonEventArgs e)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            DirItem di = DirList.SelectedItem &lt;span class="kwrd"&gt;as&lt;/span&gt; DirItem;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//資料夾的話，繼續展開&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (di.IsFolder || di.Id == goUpperSymbol) ListFiles(di.Id);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//否則試著下載回來檢視&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                DirItem.Current = di; &lt;span class="rem"&gt;//將此DirItem設為Current&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                NavigationService.Navigate(&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(&lt;span class="str"&gt;&amp;quot;/Viewer.xaml&amp;quot;&lt;/span&gt;, UriKind.Relative));&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;為了讓程式更結構化一點，我宣告了一個很簡單的SkyDrive項目物件模型: (即上面MainPage.xaml.cs中所出現DirItem、MyUserState物件的由來)&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Controls;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Documents;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Ink;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Input;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media.Animation;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Shapes;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Live;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MiniSkyDriveExplorer&lt;/pre&gt;

    &lt;pre&gt;{&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; 用以記錄REST API呼叫資訊的狀態物件&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;enum&lt;/span&gt; ApiMethod&lt;/pre&gt;

    &lt;pre class="alt"&gt;    {&lt;/pre&gt;

    &lt;pre&gt;        SkyDriveProp, &lt;span class="rem"&gt;// me/skydrive&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        SkyDriveDir,  &lt;span class="rem"&gt;// folderId/files&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        SkyDriveGetImage,&lt;/pre&gt;

    &lt;pre class="alt"&gt;        SkyDriveGetMedia&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MyUserState&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; ApiMethod Method;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; FileName;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; MyUserState(ApiMethod method)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Method = method;&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; 簡單的目錄物件&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DirItem&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//是否為資料夾&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsFolder&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; Id.StartsWith(&lt;span class="str"&gt;&amp;quot;folder&amp;quot;&lt;/span&gt;); }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//顯示名稱&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Name;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//foderId或fileId&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Id;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//上層資料夾的folderId&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ParentId;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//若為資料夾時，標示其下項目數&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Count;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//下載來源URL&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; SrcUrl;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//顯示名稱，資料夾為[folderName](count)，檔案則為fileName&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; DisplayName&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            get&lt;/pre&gt;

    &lt;pre&gt;            {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; IsFolder &amp;amp;&amp;amp; !Name.Equals(&lt;span class="str"&gt;&amp;quot;..&amp;quot;&lt;/span&gt;) ?&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;[{0}]({1})&amp;quot;&lt;/span&gt;, Name, Count) : Name;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; DirItem(&lt;span class="kwrd"&gt;string&lt;/span&gt; id, &lt;span class="kwrd"&gt;string&lt;/span&gt; name)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Id = id;&lt;/pre&gt;

    &lt;pre&gt;            Name = name;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//用此靜態屬性當作頁面間的傳遞媒介&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; DirItem Current = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//供跨頁面共用LiveConnectSession&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; LiveConnectSession LiveSession = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;除了SkyDrive清單展示，我另外做了一個Viewer.xaml，用來呈現SkyDrive中的圖片(png, jpg)及媒體檔(wmv, mp3, mp4)。 
  &lt;br /&gt;註: Windows Phone模擬器不支援部分媒體格式，測試前可先看一下&lt;a href="http://msdn.microsoft.com/en-us/library/ff462087(VS.92).aspx"&gt;MSDN文件&lt;/a&gt;，其中有些已註明This codec is unsupported in Windows Phone Emulator的格式，測試前請認清，以免在模擬器上猛試不成白花時間! (謎之聲: 這條註解怎麼隱含著強烈的怨念?)&lt;/p&gt;

&lt;p&gt;要從SkyDrive下載檔案，可以透過LiveConnectClient.DownloadAsync輕鬆達成，在client_DownloadCompleted事件中可以直接取得byte[]，十分方便。媒體檔的部分，我還沒試出可以串流播放的做法，所以先採行的方式是先將下載結果存成IsolatedStorageFile，再叫出MediaPlayerLauncher進行播放。程式範例如下:&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Controls;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Controls;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Live;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media.Imaging;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO.IsolatedStorage;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Tasks;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; MiniSkyDriveExplorer&lt;/pre&gt;

    &lt;pre&gt;{&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Viewer : PhoneApplicationPage&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Viewer()&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            InitializeComponent();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] imgExts = &lt;span class="str"&gt;&amp;quot;png,jpg&amp;quot;&lt;/span&gt;.Split(&lt;span class="str"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] mediaExts = &lt;span class="str"&gt;&amp;quot;wmv,mp3,mp4&amp;quot;&lt;/span&gt;.Split(&lt;span class="str"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; LiveConnectClient client = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; PhoneApplicationPage_Loaded(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (DirItem.LiveSession == &lt;span class="kwrd"&gt;null&lt;/span&gt; || DirItem.Current == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//建立LiveConnectClient&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            client = &lt;span class="kwrd"&gt;new&lt;/span&gt; LiveConnectClient(DirItem.LiveSession);&lt;/pre&gt;

    &lt;pre&gt;            client.DownloadCompleted += &lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;LiveDownloadCompletedEventArgs&amp;gt;(&lt;/pre&gt;

    &lt;pre&gt;                client_DownloadCompleted);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//清空內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            ContentPanel.Children.Clear();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//取得要檢視的檔案&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            DirItem di = DirItem.Current;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            ApplicationTitle.Text = di.Name;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//由副檔名決定開啟方式&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt; ext = System.IO.Path.GetExtension(di.Name).ToLower().TrimStart(&lt;span class="str"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (imgExts.Contains(ext))&lt;/pre&gt;

    &lt;pre class="alt"&gt;                client.DownloadAsync(di.SrcUrl, &lt;span class="kwrd"&gt;new&lt;/span&gt; MyUserState(ApiMethod.SkyDriveGetImage));&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (mediaExts.Contains(ext))&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//針對媒體檔，比較貼心的方式是透過Streaming方式播放&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//此處只簡單示範整個檔案下載完畢後才撥放&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                client.DownloadAsync(di.SrcUrl,&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;new&lt;/span&gt; MyUserState(ApiMethod.SkyDriveGetMedia) { FileName = di.Name });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                ContentPanel.Children.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; TextBox() { Text = &lt;span class="str"&gt;&amp;quot;Unsupported File Type&amp;quot;&lt;/span&gt; });&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;void&lt;/span&gt; client_DownloadCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, LiveDownloadCompletedEventArgs e)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            MyUserState state = e.UserState &lt;span class="kwrd"&gt;as&lt;/span&gt; MyUserState;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (state == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;switch&lt;/span&gt; (state.Method)&lt;/pre&gt;

    &lt;pre&gt;            {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; ApiMethod.SkyDriveGetImage:&lt;/pre&gt;

    &lt;pre&gt;                    BitmapImage bmp = &lt;span class="kwrd"&gt;new&lt;/span&gt; BitmapImage();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    bmp.SetSource(e.Result);&lt;/pre&gt;

    &lt;pre&gt;                    Image img = &lt;span class="kwrd"&gt;new&lt;/span&gt; Image();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    img.Source = bmp;&lt;/pre&gt;

    &lt;pre&gt;                    ContentPanel.Children.Add(img);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;case&lt;/span&gt; ApiMethod.SkyDriveGetMedia:&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//此處採用將檔案下載儲存後再播放的做法&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    IsolatedStorageFileStream fs =&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        IsolatedStorageFile.GetUserStoreForApplication()&lt;/pre&gt;

    &lt;pre&gt;                        .CreateFile(state.FileName);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    e.Result.CopyTo(fs);&lt;/pre&gt;

    &lt;pre&gt;                    fs.Close();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    MediaPlayerLauncher mpl = &lt;span class="kwrd"&gt;new&lt;/span&gt; MediaPlayerLauncher()&lt;/pre&gt;

    &lt;pre&gt;                    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        Location = MediaLocationType.Data,&lt;/pre&gt;

    &lt;pre&gt;                        Media = &lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(state.FileName, UriKind.Relative)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    };&lt;/pre&gt;

    &lt;pre&gt;                    mpl.Show();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;程式實際執行結果如下，按下SignInButton後，畫面會變成Windows Live的登入畫面，使用者輸入Windows Live帳號並同意授權後:&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8688/500x375.aspx" alt="" /&gt;&amp;#160;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8689/500x375.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;程式便可透過me/skydrive REST API取得使用者SkyDrive根目錄的Id，接著使用folderId/files就可取回該目錄下所有子項目的資訊，以ListBox的方式展示出來。&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8690/500x375.aspx" alt="" /&gt;&amp;#160; &lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8691/500x375.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;檢視圖片及影片的範例:&lt;/p&gt;

&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8692/500x375.aspx" alt="" /&gt;&amp;#160; &lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8693/500x375.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;很簡單吧!? 大家一起為WP7 App加入SkyDrive支援吧!&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8701" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="WP7" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/WP7/default.aspx" /><category term="Live SDK" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Live+SDK/default.aspx" /></entry><entry><title>資安新聞筆記-開發網站前該知道的二三事</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-23-info-sec-notes-20111222.aspx" /><id>http://blog2.darkthread.net/post-2011-12-23-info-sec-notes-20111222.aspx</id><published>2011-12-22T16:04:00Z</published><updated>2011-12-22T16:04:00Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;最近有兩則資安新聞引發我的注意:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.ithome.com.tw/itadm/article.php?c=71161"&gt;金流平台坦承內控疏失導致交易資料外洩&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://news.cnyes.com/Content/20111222/KE10V7F2B0HTQ.shtml"&gt;CSDN承認部分用戶賬號面臨風險 要求修改密碼&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;資安這檔事是這樣的，平時只會覺得系統被設了一堆限制綁手綁腳，稽核單位訂下的規矩不勝其擾，防毒軟體防火牆是效能毒藥，這一切代價換來多少功效? 卻無人知曉，直到某天豬隊友忽然來報到，才後悔資安防範做得還是太少。&lt;/p&gt;  &lt;p&gt;有鑑於對於資安重要性的體驗常來自&amp;quot;多麼痛的領悟&amp;quot;，多遇個幾次，還來不及大徹大悟前，往往命就先去了半條。(甚至歷史上也曾有因資安事件而死的悲慘案例:&amp;#160; 因公司產品內含SQL Injection漏洞導致10萬個網站被摧毁，軟體公司LxLabs的老板承受不了壓力，選擇在家中&lt;a href="http://www.theregister.co.uk/2009/06/09/lxlabs_funder_death/"&gt;懸樑自盡&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;因此，由別人的血淚經驗記取教訓看起來是比較人性化的學習成長途徑，每每看到資安新聞，我都會默默整理分析，找出在這些個案中開發者犯了什麼錯? 尤其是&amp;quot;&lt;font color="#00ff00"&gt;不管哪種語言、何種平台，一旦誤觸結局都一樣慘&lt;/font&gt;&amp;quot;的觀念性錯誤! &lt;/p&gt;  &lt;p&gt;或許我們的資安工作未必做得比事件苦主好，但從案例中推敲別人可能犯下的疏失，絕對可以當成借鏡，從中吸取教訓藉以自省，而以這兩次事件為例: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;font color="#f79646"&gt;後台與前台的存取管道應加以區隔        &lt;br /&gt;&lt;/font&gt;在戰場上曝露的面積愈大，中槍風險愈高! 而Internet，就是駭客及惡意者四伏的戰場。金流平台的後台查詢報表完整出現在Google可搜尋範圍，講起來也算一種裸奔行為。       &lt;br /&gt;若後台永遠只會由辦公室內存取，就沒有必要架構在Internet可以存取的網段。若後台需開放特定但固定的廠商、客戶存取，則採事先登記，限定來源IP也是能有效縮少曝露面的方法。而最重要的是，無論如何都不應開放匿名存取! &lt;/li&gt;    &lt;li&gt;&lt;font color="#f79646"&gt;權限管控應落實於頁面層次，甚至功能層次&lt;/font&gt;       &lt;br /&gt;此點純屬猜測，因金流明細報表原本就設定可匿名存取的機率不高，推測最後曝露的原因來自權限管控設計不良。舉個例子來說，曾看過某個兩光的權限設計只做在選單層次，有權限的人才看得到選項，從URL導向特定網頁。設計者天真地以為: 會到這個網頁來的，一定是選單有出現選項，知道URL才會過來，所以網頁本身未再次檢查權限。假設，若某使用者權限被拔除，但仍可在瀏覽歷史記錄中找到該網址呢? 如果某個有權限使用者把URL記在Word裡，寄給別人參考或檔案被人偷走呢?       &lt;br /&gt;選單固然應依使用者權限決定是否顯示選項連結，但在頁面中應該再次檢查使用者身分與權限才是王道，甚至若頁面中各功能權限有別時，別只是把不能用的鈕隱藏起來就算了，應在執行該功能前再做一次檢查。 &lt;/li&gt;    &lt;li&gt;&lt;font color="#f79646"&gt;不要用明碼或加密形式儲存密碼，雜湊才是王道&lt;/font&gt;       &lt;br /&gt;CSDN密碼外洩的最大敗筆在於密碼是明碼!      &lt;br /&gt;如果你有幸被賦與設計密碼相關資料表Schema的重責大任，請切記! 若你設計用明碼方式儲存密碼，你是大豬頭；若你決定用加密方式儲存密碼，你還是豬頭!       &lt;br /&gt;較佳的做法是只保存密碼的雜湊值，而不保存密碼本身(明碼或加密後都算)，未來可供比對使用者輸入的密碼是否吻合，卻無法用以反推密碼為何。      &lt;br /&gt;如此，使用者不用擔心密碼外洩被第三者知道，系統也不必承擔密碼內容有可能外流的風險。      &lt;br /&gt;儲存加密後的密碼雖具初步防護力，一旦加密演算法及金鑰一併失守，照樣一次輸個精光。       &lt;br /&gt;那麼，系統不知道使用者密碼，使用者忘記密碼時怎麼辦? 重寄密碼給使用者做為提醒? 請不要再當豬頭了。忘記密碼時，請重設一組隨機密碼寄給使用者，允許其在一段時間內使用它登入系統重設新密碼，要把握&amp;quot;&lt;font color="#00ff00"&gt;系統永遠不知道使用者的密碼為何，只有權重新指定&lt;/font&gt; &amp;quot;的重要原則。&lt;/li&gt;    &lt;li&gt;&lt;font color="#f79646"&gt;不要輕忽備份保存的安全性&lt;/font&gt;       &lt;br /&gt;有些線上系統戎備森嚴，受防火牆層層包圍，想存取需通過層層查驗。當線上資料備分出來後，縱然與線上資料價值相去不遠，但一被封裝在外表平凡的磁帶匣或肥大乏味的ZIP/BAK檔裡，就很容易讓人忽略其重要性與保密需求，於是存取管控嚴謹性一落千丈，甚至為了挪出空間，就隨著一群檔案被移置到某個公共空間，搞不好還被不知情第三者燒成光碟以清出HD空間，又在某天被丟進回收箱... &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;建議所有寫網站的朋友，不管你用的是什麼語言工具，什麼伺服器平台，都要勤於吸收資安相關知識，對於資安新聞(要用心找，像金流平台裸奔的消息就在一片前縣長夫人看丁字褲的新聞中被無視了)要保持敏銳度，很多的資安惡習或錯誤觀念，都是不受程式、平台疆界限制的，由別人的過失中吸收經驗，才能減少犯錯，降低當上&lt;a href="http://blog.darkthread.net/post-2011-03-30-fragile-security.aspx"&gt;豬隊友&lt;/a&gt;的機會!&lt;/p&gt;  &lt;p&gt;最後，不能免俗地還是要政策宣導一下，雖然未在本次事件中露臉，但如果你還不認識足以奪走人命的資安事件一哥--&lt;a href="http://blog.darkthread.net/post-2008-05-07-are-your-website-naked.aspx"&gt;SQL Injection&lt;/a&gt;，或者能用Parameter寫SQL指令時還是堅持一定自己組字串，請立即雙手打上石膏裝殘，先不要寫任何程式以免禍害千年，感謝您的配合~~ &lt;/p&gt;  &lt;p&gt;&lt;font color="#f79646"&gt;【延伸閱讀】&lt;/font&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;其他資安時事分析: &lt;a href="http://blog.darkthread.net/post-2011-03-30-fragile-security.aspx"&gt;資安表示: 不怕神一樣的對手...&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blog.darkthread.net/post-2008-05-07-are-your-website-naked.aspx"&gt;你的網站正在裸奔嗎?&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/04/13/asp-net-security-guide.aspx"&gt;ASP.NET防駭指南&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8699" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Security" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Security/default.aspx" /></entry><entry><title>Live SDK筆記4-SkyDrive Explorer, ASP.NET版</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-16-live-sdk-notes-4.aspx" /><id>http://blog2.darkthread.net/post-2011-12-16-live-sdk-notes-4.aspx</id><published>2011-12-15T17:11:08Z</published><updated>2011-12-15T17:11:08Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;在試過&lt;a href="http://blog.darkthread.net/post-2011-12-15-live-sdk-notes-3.aspx"&gt;ASP.NET整合Live ID登入&lt;/a&gt;後，只取回使用者姓名跟MSN大頭照感覺有點像辦家家酒不夠刺激，Live SDK應該拿來搞點實用的功能才過癮 -- 例如: 瀏覽SkyDrive文件與檔案。&lt;/p&gt;  &lt;p&gt;Live Connect API提供了SkyDrive相關的REST API，只要取得Access Token，便可透過API瀏覽、讀取、變更、刪除SkyDrive下的文件、相片、影音等檔案，MSDN上有兩篇不錯的教學文章(&lt;a href="http://msdn.microsoft.com/zh-tw/windowslive/hh561489.aspx"&gt;入門概念&lt;/a&gt;、&lt;a href="http://msdn.microsoft.com/zh-tw/windowslive/hh561740"&gt;檔案存取API&lt;/a&gt;)可當成動手前的參考。&lt;/p&gt;  &lt;p&gt;我的構想是在使用者以Live ID登入時，增加SkyDrive讀取的範圍請求，經同意後，程式先展開使用者SkyDrive的根目錄，列出資料夾與檔案；點選資料夾項目則可展開該資料夾，列出其下檔案與子資料夾；若為檔案，則點選可檢視或下載其內容。(瀏覽器依附檔名自行決定檢視或下載，例如: gif, png, jpg, txt, htm可直接開啟，zip, rar則是下載另存) 簡單來說，就是實現一個只能用來檢視的超陽春SkyDrive檔案總管! &lt;/p&gt;  &lt;p&gt;整理需用到的SkyDrive REST API有:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;httqs://apis.live.net/v5.0/me/skydrive?access_token=ACCESS_TOKEN      &lt;br /&gt;取得使用者SkyDrive主資料夾的folderId &lt;/li&gt;    &lt;li&gt;httqs://apis.live.net/v5.0/folderId/files?access_token=ACCESS_TOKEN      &lt;br /&gt;列出指定資料夾下的資料夾及檔案項目清單 &lt;/li&gt;    &lt;li&gt;httqs://apis.live.net/v5.0/fileId/content?access_token=ACCESS_TOKEN      &lt;br /&gt;取得檔案屬性，其中location屬性會指向一個可直接下載檔案的URL(格式如: httq://storage.live.com/s1pZfcNs…很長，省略...SotQ/Seattle.JPG)，連向它就可直接下載檔案內容。值得注意的是，前述包含冗長編碼的URL是所謂的Preauthenticated URL，意思是只要得知此連結，將不再做任何權限檢查，任何人都可用它直接下載檔案內容，在開發應用時宜留意此特性，做適當管控及避免外流。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;是的，要做到SkyDrive檔案檢視功能，就只需要上述三個API即可搞定。&lt;/p&gt;  &lt;p&gt;至於JavaScript操作的部分，我結合了jQuery BBQ外掛的&lt;a href="http://blog.darkthread.net/post-2011-09-22-ajax-goback.aspx"&gt;AJAX Hisotry功能&lt;/a&gt;，在檢視不同資料夾時指定不同的state，以做到按瀏覽器回上頁也可回上層目錄的效果。除此之外，就是蠻單純的jQuery技巧，補充說明我寫在註解中，程式碼如下:&lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt; &lt;span class="attr"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;SkyDrive Test&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;text/javascript&amp;#39;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre class="alt"&gt;     &lt;span class="attr"&gt;src&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.js&amp;#39;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;   &lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;script src=&lt;span class="str"&gt;&amp;quot;jquery.ba-bbq.js&amp;quot;&lt;/span&gt; type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;script type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;        $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//取得Access Token，未成功登入時傳回null&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;function&lt;/span&gt; getAccessToken() {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; session = WL.getSession();&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (session != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; !session.error)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; session.access_token;&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//AJAX History支援，state改變時顥示不同資料夾內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            window.lastId = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            $(window).bind(&lt;span class="str"&gt;&amp;quot;hashchange&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; (e) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; f = e.getState(&lt;span class="str"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (f) listFiles(f);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; goUpper = &lt;span class="str"&gt;&amp;quot;回上層&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; frmViewer = document.getElementById(&lt;span class="str"&gt;&amp;quot;frmViewer&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//列出檔案&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;function&lt;/span&gt; listFiles(folderId) {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (!getAccessToken()) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//REST folderId/files可列出資料夾下的檔案清單&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                WL.api({&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    path: folderId + &lt;span class="str"&gt;&amp;quot;/files&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                }, &lt;span class="kwrd"&gt;function&lt;/span&gt; (res) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;var&lt;/span&gt; $ul = $(&lt;span class="str"&gt;&amp;quot;&amp;lt;ul class=&amp;#39;f-item&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//folderId若以&amp;quot;.&amp;quot;分成三段，代表非根目錄&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//增加一個&amp;quot;回上層&amp;quot;連結&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (folderId.split(&lt;span class="str"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;).length &amp;gt; 2)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        $ul.append(&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;&amp;lt;img src=&amp;#39;images/parent.png&amp;#39; class=&amp;#39;icon&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt; +&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            goUpper + &lt;span class="str"&gt;&amp;quot;&amp;lt;/li&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//每個資料夾或檔案對應一個li&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;var&lt;/span&gt; i = 0; i &amp;lt; res.data.length; i++) {&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="kwrd"&gt;var&lt;/span&gt; item = res.data[i];&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;var&lt;/span&gt; $li = $(&lt;span class="str"&gt;&amp;quot;&amp;lt;li /&amp;gt;&amp;quot;&lt;/span&gt;, {&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="str"&gt;&amp;quot;data-id&amp;quot;&lt;/span&gt;: item.id,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="str"&gt;&amp;quot;data-count&amp;quot;&lt;/span&gt;: item.count,&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="str"&gt;&amp;quot;data-name&amp;quot;&lt;/span&gt;: item.name,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="str"&gt;&amp;quot;data-parent-id&amp;quot;&lt;/span&gt;: item.parent_id,&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="str"&gt;&amp;quot;data-link&amp;quot;&lt;/span&gt;: item.link&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        });&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="rem"&gt;//id若為folder****，表示為資料夾&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;var&lt;/span&gt; isFolder = item.id.indexOf(&lt;span class="str"&gt;&amp;quot;folder&amp;quot;&lt;/span&gt;) == 0;&lt;/pre&gt;

    &lt;pre&gt;                        $li.html(&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            &lt;span class="str"&gt;&amp;quot;&amp;lt;img class=&amp;#39;icon&amp;#39; src=&amp;#39;images/&amp;quot;&lt;/span&gt; +&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="rem"&gt;//資料夾與檔案Icon不同&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            (isFolder ? &lt;span class="str"&gt;&amp;quot;folder&amp;quot;&lt;/span&gt; : &lt;span class="str"&gt;&amp;quot;save&amp;quot;&lt;/span&gt;) + &lt;span class="str"&gt;&amp;quot;.png&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt; +&lt;/pre&gt;

    &lt;pre&gt;                            &lt;span class="str"&gt;&amp;quot;&amp;lt;span class=&amp;#39;item-name&amp;#39;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        .find(&lt;span class="str"&gt;&amp;quot;.item-name&amp;quot;&lt;/span&gt;).text(&lt;/pre&gt;

    &lt;pre&gt;                             item.name +&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//資料夾時，加顯示其下檔案數量&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                            (isFolder ? &lt;span class="str"&gt;&amp;quot;(&amp;quot;&lt;/span&gt; + item.count + &lt;span class="str"&gt;&amp;quot;)&amp;quot;&lt;/span&gt; : &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        );&lt;/pre&gt;

    &lt;pre&gt;                        $ul.append($li);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                    $(&lt;span class="str"&gt;&amp;quot;#dFolder&amp;quot;&lt;/span&gt;).empty().append($ul);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    frmViewer.src = &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;                });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//下載檔案內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;function&lt;/span&gt; downloadFile(id, name) {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="rem"&gt;//REST fileId/content可取得下載檔案內容用的連結&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                WL.api({&lt;/pre&gt;

    &lt;pre&gt;                    path: id + &lt;span class="str"&gt;&amp;quot;/content&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                }, &lt;span class="kwrd"&gt;function&lt;/span&gt; (res) {&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (res.error)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        alert(res.error.message);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//res.location指向可直接下載檔案二進位內容的連結&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="rem"&gt;//指定成為IFrame.src，&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//可直接顯示或另存檔案(由瀏覽器依附檔名決定)&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                        frmViewer.src = res.location;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    }&lt;/pre&gt;

    &lt;pre&gt;                });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//點選檔案項目觸發事件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#dFolder&amp;quot;&lt;/span&gt;).on(&lt;span class="str"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;li&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; $li = $(&lt;span class="kwrd"&gt;this&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//若為回上層，則透過記憶中的前一層目錄回去&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; ($li.text() == goUpper) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    $.bbq.pushState({ f: lastId });&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                }&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; id = $li.data(&lt;span class="str"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (id.indexOf(&lt;span class="str"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;) == 0)&lt;/pre&gt;

    &lt;pre&gt;                    downloadFile(id);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//保留目前的folderId供稍後回上層用&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    lastId = $.bbq.getState(&lt;span class="str"&gt;&amp;quot;f&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                    $.bbq.pushState({ f: id });&lt;/pre&gt;

    &lt;pre class="alt"&gt;                }&lt;/pre&gt;

    &lt;pre&gt;            });&lt;/pre&gt;

    &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#bList&amp;quot;&lt;/span&gt;).click(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (!getAccessToken()) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;    &amp;lt;style&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        table { width: 400px; }&lt;/pre&gt;

    &lt;pre&gt;        td { border: 1px solid gray; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        div { margin: 2px; }&lt;/pre&gt;

    &lt;pre&gt;        #dFolder li { color: white; text-decoration: underline; cursor: pointer; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        .bw-style { background-color: Black; color: White; }&lt;/pre&gt;

    &lt;pre&gt;        .bw-style .icon { width: 16px; height: 16px; vertical-align: middle; }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;/head&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;body&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;h1&amp;gt;SkyDrive Test&amp;lt;/h1&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;table&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;tr&amp;gt;&amp;lt;td style=&lt;span class="str"&gt;&amp;quot;width: 120px; vertical-align: top;&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;meName&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt;=&lt;span class="str"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;meImg&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&lt;span class="str"&gt;&amp;quot;vertical-align: top;&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt;=&lt;span class="str"&gt;&amp;#39;bw-style&amp;#39;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;dFolder&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan=&lt;span class="str"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;iframe style=&lt;span class="str"&gt;&amp;quot;width: 100%; height: 400px;&amp;quot;&lt;/span&gt; id=&lt;span class="str"&gt;&amp;quot;frmViewer&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;/table&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;script src=&lt;span class="str"&gt;&amp;quot;wl.debug.js&amp;quot;&lt;/span&gt; type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;script type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;    $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        WL.Event.subscribe(&lt;span class="str"&gt;&amp;quot;auth.login&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; session = WL.getSession();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (session.error)&lt;/pre&gt;

    &lt;pre&gt;                alert(&lt;span class="str"&gt;&amp;quot;Error:&amp;quot;&lt;/span&gt; + session.error);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; token = session != &lt;span class="kwrd"&gt;null&lt;/span&gt; ? session.access_token : &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (token != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;var&lt;/span&gt; url = &lt;span class="str"&gt;&amp;quot;https://apis.live.net/v5.0/me/picture?access_token=&amp;quot;&lt;/span&gt; &lt;/pre&gt;

    &lt;pre class="alt"&gt;                    + escape(token);&lt;/pre&gt;

    &lt;pre&gt;                    $(&lt;span class="str"&gt;&amp;quot;#meImg&amp;quot;&lt;/span&gt;).html(&lt;span class="str"&gt;&amp;quot;&amp;lt;img src=&amp;#39;&amp;quot;&lt;/span&gt; + url + &lt;span class="str"&gt;&amp;quot;&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    WL.api({ path: &lt;span class="str"&gt;&amp;quot;me&amp;quot;&lt;/span&gt;, method: &lt;span class="str"&gt;&amp;quot;get&amp;quot;&lt;/span&gt; }, &lt;span class="kwrd"&gt;function&lt;/span&gt; (response) {&lt;/pre&gt;

    &lt;pre&gt;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!response.error) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                            $(&lt;span class="str"&gt;&amp;quot;#meName&amp;quot;&lt;/span&gt;).html(response.name);&lt;/pre&gt;

    &lt;pre&gt;                        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    });&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//取得SkyDrive Id，透過AJAX History方式觸發瀏覽&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    $.bbq.pushState({ f: &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt; });&lt;/pre&gt;

    &lt;pre&gt;                    WL.api({&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        path: &lt;span class="str"&gt;&amp;quot;me/skydrive&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    }, &lt;span class="kwrd"&gt;function&lt;/span&gt; (prop) {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        $.bbq.pushState({ f: prop.id });&lt;/pre&gt;

    &lt;pre&gt;                    });&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre&gt;                }&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;        });&lt;/pre&gt;

    &lt;pre class="alt"&gt;        WL.Event.subscribe(&lt;span class="str"&gt;&amp;quot;auth.logout&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

    &lt;pre&gt;            $(&lt;span class="str"&gt;&amp;quot;#meImg,#meName&amp;quot;&lt;/span&gt;).html(&lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;        });&lt;/pre&gt;

    &lt;pre&gt;        WL.init({&lt;/pre&gt;

    &lt;pre class="alt"&gt;            client_id: &lt;span class="str"&gt;&amp;quot;0000000048076EE5&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;            redirect_uri: &lt;span class="str"&gt;&amp;quot;http://www.darkthread.net/LiveSDK/callback.aspx&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;            scope: [&lt;span class="str"&gt;&amp;quot;wl.signin&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;wl.basic&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;wl.offline_access&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;wl.skydrive&amp;quot;&lt;/span&gt; ],&lt;/pre&gt;

    &lt;pre&gt;            response_type: &lt;span class="str"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        });&lt;/pre&gt;

    &lt;pre&gt;        WL.ui({&lt;/pre&gt;

    &lt;pre class="alt"&gt;            name: &lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;            element: &lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        });&lt;/pre&gt;

    &lt;pre&gt;    });&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;程式執行起來的樣子如下圖，右方是檔案清單，下方放了一個IFrame。當點選資料夾(前方圖示為資料夾)時，清單會變成該資料夾展開後的項目；當點選檔案清單中檔案項目(前方Icon為小磁片)，IFrame.src將指向該檔案的下載網址，若是圖檔可直接顯示，其他如ZIP檔等，則可另存新檔。(但有個小缺點，另存新檔時，SkyDrive伺服器傳回內容未提供檔名，預設URL中又臭又長像亂數的編碼預設會被當成檔名，故需要人工另外命名再存檔，此點可透過另外寫個Download Proxy ASPX來解決，但為避免失焦，並未納入範例中）&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8680/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;還有一點值得注意，相較於前次整合Live ID登入範例，這回我們還增加了wl.skydrive讀取範圍(Scope，可參見&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;前文&lt;/a&gt;)，因此同意頁面(Consent Page)可看到SkyDrive的提示(下圖紅框)。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8679/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;另外，要弄出上圖中文版登入及同意網頁有個小訣竅: 依文件的&lt;a href="http://msdn.microsoft.com/zh-tw/windowslive/hh278357"&gt;說法&lt;/a&gt;，我們可由&lt;a title="http://js.live.net/v5.0/zh-tw/wl.js" href="http://js.live.net/v5.0/zh-tw/wl.js"&gt;http://js.live.net/v5.0/zh-tw/wl.js&lt;/a&gt;取得正體中文版的JavaScript Library，但目前這個版本的最下方仍設定了wl_app._locale = &amp;quot;en&amp;quot;;，其中的訊息也都還是英文。因此，呼叫Live帳號登入視窗時仍會顯示英文介面。暫時解法是將wl.js另存一份，並修改為wl_app._locale=&amp;quot;zh-tw&amp;quot;;，網頁改用此修改版就可以獲得中文版登入及同意畫面囉! 但有個缺點，未來若wl.js改版，修改版wl.js不會自動更新，我想不久後，等微軟RD真的落實wl.js多國語系化，就不用煩惱了。&lt;/p&gt;

&lt;p&gt;程式執行後，我們點開ForApps資料夾，可以看到其下的資料夾結構，共有兩個圖檔，圖檔也可以點選檢視其內容:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8681/original.aspx" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;跟SkyDrive網頁的瀏覽結果比對，我們查到的結果與ForApps目錄下的檔案項目與內容相符，一個陽春版SkyDrive檔案檢視工具就算大功告成囉!!&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8682/original.aspx" alt="" /&gt;&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8683" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Live SDK" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Live+SDK/default.aspx" /></entry><entry><title>Live SDK筆記3-使用Live ID登入ASP.NET網站</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-15-live-sdk-notes-3.aspx" /><id>http://blog2.darkthread.net/post-2011-12-15-live-sdk-notes-3.aspx</id><published>2011-12-14T23:13:40Z</published><updated>2011-12-14T23:13:40Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;Live SDK提供了包含ASP.NET、PHP、WP7、Silverlight、Android、iOS等多個平台的&lt;a href="https://github.com/liveservices/LiveSDK/tree/master/Samples"&gt;範例程式&lt;/a&gt;。既然如此，光說不練未免暴殄天物，就選擇我最熟悉的ASP.NET來實際演練吧!&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;在github可以取得ASP.NET&lt;a href="https://github.com/liveservices/LiveSDK/tree/master/Samples/Asp.net"&gt;範例程式&lt;/a&gt;，其中OAuthSample只需要一個ASPX，一個HTML，配合Live Connect的現成Javascript Library，可快速實現由Client端取得使用者MSN Messenger姓名及個人大頭照，感覺蠻有趣的，我們就用它當敲門磚來練習。       &lt;br /&gt;在github可取得Callback.aspx, Callback.aspx.cs及default.html，其中Callback.aspx.cs需要修改，需填入Client ID、Reidrect Page網址以及Client Secret:       &lt;div class="BlogCodeBlock"&gt;       &lt;div class="csharpcode"&gt;         &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Callback : System.Web.UI.Page&lt;/pre&gt;

        &lt;pre&gt;    {&lt;/pre&gt;

        &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; wlCookie = &lt;span class="str"&gt;&amp;quot;wl_auth&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

        &lt;pre&gt;&amp;#160;&lt;/pre&gt;

        &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; clientId = &lt;span class="str"&gt;&amp;quot;0000000048076EE5&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

        &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; callback = &lt;/pre&gt;

        &lt;pre class="alt"&gt;        &lt;span class="str"&gt;&amp;quot;http://www.darkthread.net/AspNet4/LiveSDK/callback.aspx&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

        &lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; clientSecret = &lt;span class="str"&gt;&amp;quot;3n6YXboo5wnfooJwrbo0800092000dPd&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
      &lt;/div&gt;
    &lt;/div&gt;
default.html的部分原本只需指定Client ID及Redirect Page網址，但我加入jQuery做了比較大幅度的改寫，讓程式簡潔一些: :P 

    &lt;div class="BlogCodeBlock"&gt;
      &lt;div class="csharpcode"&gt;
        &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt; &lt;span class="attr"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Windows Live Test&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;        &lt;span class="rem"&gt;&amp;lt;!--&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&lt;span class="rem"&gt;        .Name&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="rem"&gt;        {&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&lt;span class="rem"&gt;            font-family: Segoe UI, Verdana, Tahoma, Helvetica, Arial, sans-serif;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="rem"&gt;            font-weight: bold;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&lt;span class="rem"&gt;        }&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="rem"&gt;        --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;text/javascript&amp;#39;&lt;/span&gt; &lt;/pre&gt;

        &lt;pre class="alt"&gt;     &lt;span class="attr"&gt;src&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.js&amp;#39;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;   &lt;/pre&gt;

        &lt;pre&gt;&amp;lt;/head&amp;gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&amp;lt;body&amp;gt;&lt;/pre&gt;

        &lt;pre&gt;&amp;lt;h1&amp;gt;Windows Live Test&amp;lt;/h1&amp;gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&amp;lt;div&amp;gt;&lt;/pre&gt;

        &lt;pre&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;meName&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt;=&lt;span class="str"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

        &lt;pre&gt;&amp;lt;div id=&lt;span class="str"&gt;&amp;quot;meImg&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;

        &lt;pre&gt;&amp;lt;script src=&lt;span class="str"&gt;&amp;quot;//js.live.net/v5.0/wl.js&amp;quot;&lt;/span&gt; type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&amp;lt;script type=&lt;span class="str"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&amp;gt;&lt;/pre&gt;

        &lt;pre&gt;    $(&lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

        &lt;pre class="alt"&gt;        WL.Event.subscribe(&lt;span class="str"&gt;&amp;quot;auth.login&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

        &lt;pre&gt;            &lt;span class="kwrd"&gt;var&lt;/span&gt; session = WL.getSession();&lt;/pre&gt;

        &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (session.error)&lt;/pre&gt;

        &lt;pre&gt;                alert(&lt;span class="str"&gt;&amp;quot;Error:&amp;quot;&lt;/span&gt; + session.error);&lt;/pre&gt;

        &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;

        &lt;pre&gt;                &lt;span class="kwrd"&gt;var&lt;/span&gt; token = session != &lt;span class="kwrd"&gt;null&lt;/span&gt; ? session.access_token : &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

        &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (token != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;

        &lt;pre&gt;                    &lt;span class="kwrd"&gt;var&lt;/span&gt; url = &lt;/pre&gt;

        &lt;pre class="alt"&gt;        &lt;span class="str"&gt;&amp;quot;https://apis.live.net/v5.0/me/picture?access_token=&amp;quot;&lt;/span&gt; + escape(token);&lt;/pre&gt;

        &lt;pre&gt;                    $(&lt;span class="str"&gt;&amp;quot;#meImg&amp;quot;&lt;/span&gt;).html(&lt;span class="str"&gt;&amp;quot;&amp;lt;img src=&amp;#39;&amp;quot;&lt;/span&gt; + url + &lt;span class="str"&gt;&amp;quot;&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

        &lt;pre class="alt"&gt;                    WL.api({ path: &lt;span class="str"&gt;&amp;quot;me&amp;quot;&lt;/span&gt;, method: &lt;span class="str"&gt;&amp;quot;get&amp;quot;&lt;/span&gt; }, &lt;span class="kwrd"&gt;function&lt;/span&gt; (response) {&lt;/pre&gt;

        &lt;pre&gt;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!response.error) {&lt;/pre&gt;

        &lt;pre class="alt"&gt;                            $(&lt;span class="str"&gt;&amp;quot;#meName&amp;quot;&lt;/span&gt;).html(&lt;/pre&gt;

        &lt;pre&gt;                                response.first_name + &lt;span class="str"&gt;&amp;quot; &amp;quot;&lt;/span&gt; + response.last_name);&lt;/pre&gt;

        &lt;pre class="alt"&gt;                        }&lt;/pre&gt;

        &lt;pre&gt;                    });&lt;/pre&gt;

        &lt;pre class="alt"&gt;                }&lt;/pre&gt;

        &lt;pre&gt;            }&lt;/pre&gt;

        &lt;pre class="alt"&gt;        });&lt;/pre&gt;

        &lt;pre&gt;        WL.Event.subscribe(&lt;span class="str"&gt;&amp;quot;auth.logout&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;function&lt;/span&gt; () {&lt;/pre&gt;

        &lt;pre class="alt"&gt;            $(&lt;span class="str"&gt;&amp;quot;#meImg,#meName&amp;quot;&lt;/span&gt;).html(&lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

        &lt;pre&gt;        });&lt;/pre&gt;

        &lt;pre class="alt"&gt;        WL.init({&lt;/pre&gt;

        &lt;pre&gt;            client_id: &lt;span class="str"&gt;&amp;quot;0000000048076EE5&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

        &lt;pre class="alt"&gt;            redirect_uri: &lt;span class="str"&gt;&amp;quot;http://www.darkthread.net/AspNet4/LiveSDK/callback.aspx&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

        &lt;pre&gt;            scope: [&lt;span class="str"&gt;&amp;quot;wl.signin&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;wl.basic&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;wl.offline_access&amp;quot;&lt;/span&gt;],&lt;/pre&gt;

        &lt;pre class="alt"&gt;            response_type: &lt;span class="str"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;        });&lt;/pre&gt;

        &lt;pre class="alt"&gt;        WL.ui({&lt;/pre&gt;

        &lt;pre&gt;            name: &lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

        &lt;pre class="alt"&gt;            element: &lt;span class="str"&gt;&amp;quot;signin&amp;quot;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;        });&lt;/pre&gt;

        &lt;pre class="alt"&gt;    });&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;script&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

        &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
      &lt;/div&gt;
    &lt;/div&gt;
網頁在一開始載入wl.js(如果要研究或偵錯，可改用wl.debug.js未壓縮版)，複雜工作都已被封裝在現成的Javascript函數中，我們只需要: 

    &lt;br /&gt;1) WL.Event.subscribe(&amp;quot;auth.login&amp;quot;, fn) 指定使用者已登入或登入完成時要執行的動作 

    &lt;br /&gt;2) WL.Event.subscribe(&amp;quot;auth.logout&amp;quot;, fn) 指定使用者登出時要執行的動作 

    &lt;br /&gt;3) WL.ui() 指定在&amp;lt;div id=&amp;quot;signin&amp;quot;&amp;gt;自動加上登入登出按鈕 

    &lt;br /&gt;4) WL.init() 指定Client ID, Redirect Page及存取資料範圍(Scope) 

    &lt;br /&gt;(範圍部分指定了三項: wl.signin是指當使用者已登入過其他Live Connect服務時在本網站可自動登入, wl.basic用來存取基本資料, wl.offline_access用來確保可在使用者未登入MSN時也能取得資訊，關於範圍的定義，&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;前文&lt;/a&gt;有較多介紹) &lt;/li&gt;

  &lt;li&gt;WL.init()時指定response_type: &amp;quot;code&amp;quot;，因此將會依循Authorization Code Grant Flow 運作(可參考&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;前文&lt;/a&gt;)，運作時會先跳出Live帳號的登入及同意授權畫面(Consent Page)，當若使用者同意後，網頁會被導向Redirect Page(Callback.aspx)以Query Sting帶入code=authorization_code參數。Callback.aspx.cs中有邏輯會將所傳入的authorizationcode配合Client ID、Client Secret送至httqs://oauth.live.com/token驗證，取得Access Token並寫入Cookie，前端即可應用它進行後續存取。 &lt;/li&gt;

  &lt;li&gt;要測試偵錯時有個小技巧，因Live Connect API只接受當初註冊Domain的網頁作為Redirect URL，因此若要在本機執行偵錯，指向localhost是不可行的。較簡單的克服方法是修改system32/dirvers/etc/hosts，將註冊的Domain(我的例子中是&lt;a href="http://www.darkthread.net"&gt;www.darkthread.net&lt;/a&gt;)暫時指向127.0.0.1，如此httq://www.darkthread.net將被導向本機的IIS，導向Redirect Page時，實際上會在本機執行。 &lt;/li&gt;

  &lt;li&gt;將程式部署到IIS網站上，開啟default.html即可看到如下畫面，wl.js依我們的指示在&amp;lt;div id=&amp;quot;singin&amp;quot;&amp;gt;裡放入了一顆Sign in鈕，按下後會彈出來自live.com，大家很熟悉的登入畫面。這代表在整個授權過程中，我們的程式完全不參與登入過程，不會接觸到帳號密碼，使用者也不必擔心自己的密碼透露給第三方網站知道，這是OAuth機制的重要精神所在。 
    &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8660/640x480.aspx" alt="" /&gt; &lt;/li&gt;

  &lt;li&gt;登入完成後，使用者會進入授權同意畫面(Consent Page)，其中列舉的授權項目，一一對應到程式所要求的wl.sigin, wl.basic, wl.offline_access等範圍。 
    &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8661/640x480.aspx" alt="" /&gt; &lt;/li&gt;

  &lt;li&gt;一旦使用者同意授權，WL.Event.subscribe(&amp;quot;auth.login&amp;quot;, fn) 的函數會被觸發，但此時Callback.aspx已先在背地裡完成一連串動作: (即&lt;a href="http://blog.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx"&gt;前文&lt;/a&gt;所說的Authorization Code Grant Flow) 

    &lt;br /&gt;* Live Connect導向Callback.aspx?code=authorizationcode 

    &lt;br /&gt;* Callback.aspx接收authorizationcode，加上Client ID, Client Secrect當成參數呼叫httqs://oauth.live.com/token，取得Access Token 

    &lt;br /&gt;* 將Access Token以Cookie方式傳回 

    &lt;br /&gt;使用Visual Studio中斷點偵錯或用Fiddler可以觀察到這段過程: 

    &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8674/640x480.aspx" alt="" /&gt; &lt;/li&gt;

  &lt;li&gt;由於Callback.aspx已透過Cookie傳遞了Access Token，使用JavaScript由WL.getSession().access_token可取得Access Token，呼叫httqs://apis.live.net/v5.0/me/picture?access_token=…可以顯示使用者的MSN大頭照，使用WL.api()呼叫&amp;quot;me&amp;quot;取回&lt;a href="http://msdn.microsoft.com/en-us/library/hh243648.aspx#user"&gt;User Object&lt;/a&gt;(背後是呼叫httqs://apis.live.net/v5.0/me/ REST)，可得到first_name及last_name，呈現在網頁上，就完成了Live Connect API的&amp;quot;Hello World測試&amp;quot;囉! 

    &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8662/640x480.aspx" alt="" /&gt;&amp;#160; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我放了一個&lt;a href="http://www.darkthread.net/AspNet4/LiveSDK/default.htm"&gt;線上展示&lt;/a&gt;，有興趣的人可以玩看看。&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8675" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Live SDK" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Live+SDK/default.aspx" /></entry><entry><title>Live SDK筆記2-註冊App與基本術語</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx" /><id>http://blog2.darkthread.net/post-2011-12-14-live-sdk-notes-2.aspx</id><published>2011-12-13T22:25:31Z</published><updated>2011-12-13T22:25:31Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;跟Google Map、Facebook、Plurk等API一樣，程式要引用Live Connect API前，必須先註冊以取得一組專屬Client ID與Client Secret(相當於密碼)作為識別。使用自己的Live帳號登入Live Connect Developer Center後即可&lt;a href="https://manage.dev.live.com/AddApplication.aspx?tou=1"&gt;免費申請&lt;/a&gt;(注意，Metro Style App的&lt;a href="https://manage.dev.live.com/build?wa=wsignin1.0"&gt;申請方式&lt;/a&gt;不同)。&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;使用Windows Live帳號(Live ID，即Hotmail/MSN Messenger用的帳號)登入Live Connect開發者中心，可在My Apps介面為App申請Client ID。申請時要填寫應用程式名稱以及使用語言:      &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8657/640x480.aspx" alt="" /&gt; &lt;/li&gt;    &lt;li&gt;完成後可取得一組Client ID及Client Secret，稍後在程式中會用到。      &lt;br /&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8658/640x480.aspx" alt="" /&gt; &lt;/li&gt;    &lt;li&gt;在程式型OAuth授證流程中，需要一支Callback網頁程式(稱之為Redirect Page)保存Client Secret，並與Live Connect API溝通以取得Access Token，基於安全管控理由，必須事先登錄Redirect Page所在網域。該組Client ID在使用時，將只會允許該網域下的網頁作為Redirect Page，在我的案例中，Redirect Page網頁未來會放在 &lt;a href="http://www.darkthread.net"&gt;www.darkthread.net&lt;/a&gt; 網站下，故填入&lt;a href="http://www.darkthread.net"&gt;http://www.darkthread.net&lt;/a&gt; 。(針對行動裝置或桌面程式的授權流程，因無Server端Redirect Page，可改用特殊 httqs://oauth.live.com/desktop替代之，此時Redirect domain留空，Mobile client app請選Yes。)&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/8659/640x480.aspx" alt="" /&gt; &lt;/li&gt;    &lt;li&gt;除了上述設定，還可另外自訂應用程式的名稱、Logo、服務條款、隱私權聲明等資訊，將來會出現在請使用者確認同意App要求權限範圍(Scope)的Consent Page(同意頁面)上，Live Connect開發者中心&lt;a href="http://msdn.microsoft.com/zh-tw/windowslive/hh278360"&gt;有篇文章&lt;/a&gt;提供了詳細的出現位置示意圖。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;完成以上設定，理論上可以開始應用它連接Live Connect，但在此之前，先補充一些文件常出現的術語: 註: &lt;a href="http://zh.wikipedia.org/wiki/OAuth"&gt;OAuth協定&lt;/a&gt;的技術細節蠻複雜的，在此只簡單列舉一些閱讀MSDN文件會用到的術語觀念。MVP小朱之前曾寫過&lt;a href="http://www.dotblogs.com.tw/regionbbs/Tags/OAuth/default.aspx"&gt;一系列&lt;/a&gt;介紹文章(而且還實做了一套Open Source的OAuth .NET程式庫)，另外Level Up也有一篇&lt;a href="http://www.dotblogs.com.tw/larrynung/archive/2011/04/28/23779.aspx"&gt;簡介&lt;/a&gt;，有興趣的朋友可以參考。&lt;/p&gt;  &lt;p&gt;&lt;font color="#f79646"&gt;【OAuth角色】&lt;/font&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Resource Owner      &lt;br /&gt;有權授與受保護資源存取權限的個體，例如: 一般使用者 &lt;/li&gt;    &lt;li&gt;Resource Server      &lt;br /&gt;保存及管理受保護資源的伺服器，如保存信件、連絡人資料的Hotmail伺服器、保存文件、影音相片檔的SkyDrive伺服器 &lt;/li&gt;    &lt;li&gt;Client      &lt;br /&gt;代表Resource Owner要求存取受保護資源的應用程式 &lt;/li&gt;    &lt;li&gt;Authorization Server      &lt;br /&gt;負責驗證Resource Owner身分並獲得授權的伺服器 &lt;/li&gt;    &lt;li&gt;Resource Owner&amp;#39;s Agent (User-Agent)      &lt;br /&gt;用以承載Client應用程式的平台，例如: 當Client以Javascript撰寫在瀏覽器中執行，瀏覽器即User Agent&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;font color="#f79646"&gt;【OAuth Flow】&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;依循OAuth驗證使用者身分並取得授權的流程，Live Connect支援三種不同流程(詳見&lt;a href="http://msdn.microsoft.com/en-us/library/hh243647.aspx"&gt;MSDN文件&lt;/a&gt;):&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Implicit Grant Flow      &lt;br /&gt;主要應用於網站程式或桌面程式，程式呼叫httqs://oauth.live.com/authorize?client_id=CLIENT_ID&amp;amp;scope=SCOPES&amp;amp;response_type=token&amp;amp;redirect_uri=REDIRECT_URL，Live Connect提供登入介面及授權同意網頁(Consent Page)，當取得授權後，Live Connect會導向REDIRECT_URL，並在後方加註Acess Token當成參數，程式取出Access Token後，即可用於向Resource Server存取相關資源。 &lt;/li&gt;    &lt;li&gt;Authorization Code Grant Flow      &lt;br /&gt;主要應用於伺服器對伺服器間的驗證流程，Client發出httqs://oauth.live.com/authorize?client_id=CLIENT_ID&amp;amp;scope=SCOPES&amp;amp;response_type=code&amp;amp;redirect_uri=REDIRECT_URI，接著Live Connect在識別使用者並取得同意後導向REDIRECT_URI並加上&amp;amp;code=[&lt;font color="#00ff00"&gt;authorizationcode&lt;/font&gt;](不是直接給Access Token)，REDIRECT_URI必須是伺服器可執行網頁，將code所傳入的authorizationcode配合Client ID、Client Secret送至httqs://oauth.live.com/token驗證，可取得Access Token，如此可在Access Token完全不外洩的狀況下，在伺服器端使用它向Resource Server取用資源。 &lt;/li&gt;    &lt;li&gt;Sign-In Control Flow      &lt;br /&gt;透過Live Connect提供的登入控制項封裝Implicit Grant Flow流程，只需引用相關的Javascript程式庫或.NET元件，撰寫相關事件邏輯、呼叫適當方法，即可完成登入授權，可大幅簡化自行處理驗證授權流程的細節。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;font color="#f79646"&gt;【Scope】&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;在取得授權時，必須聲明要存取的範圍(Scope)，同意網頁則會明列Client程式要求的授權範圍，使用者再決定是否同意。一般來說，最好以&amp;quot;足以運作的最小範圍&amp;quot;為原則，可減少使用者疑慮，獲得同意的機會較高，App的操作體驗也會較佳。以下是Live Connect目前定義的Scope範圍: (詳細說明可見&lt;a href="http://msdn.microsoft.com/en-us/library/hh243646.aspx"&gt;MSDN文件&lt;/a&gt;)&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;wl.basic: 取得使用者個人檔案(Profile)基本資料及連絡人 &lt;/li&gt;    &lt;li&gt;wl.offline_access: 允許在使用者離線時繼續取得資料 &lt;/li&gt;    &lt;li&gt;wl.signin: 單一簽入功能(Single-Sign-On)，若使用者已登入Live Connect則可自動登入該App &lt;/li&gt;    &lt;li&gt;wl.brithday: 取得生日 &lt;/li&gt;    &lt;li&gt;wl.calendars: 讀取行事曆 &lt;/li&gt;    &lt;li&gt;wl.calendars_update: 更新行事曆 &lt;/li&gt;    &lt;li&gt;wl.contacts_calendars: 讀取連絡人(含使用者本人)的行事曆 &lt;/li&gt;    &lt;li&gt;wl.contacts_birthday: 取得連絡人(含使用者本人)的生日 &lt;/li&gt;    &lt;li&gt;wl.contacts_create: 建立連絡人 &lt;/li&gt;    &lt;li&gt;wl.photos: 讀取使用者的相簿、照片、影音檔 &lt;/li&gt;    &lt;li&gt;wl.contacts_photos: 讀取連絡人(含使用者本人)的相簿、照片、影音檔，以其所屬標籤與留言 &lt;/li&gt;    &lt;li&gt;wl.emails: 讀取使用者私人、偏好及公司電子郵件地址 &lt;/li&gt;    &lt;li&gt;wl.events_create: 在使用者預設行事曆建立事件 &lt;/li&gt;    &lt;li&gt;wl.messenger: 允許登入Live Messenger服務 &lt;/li&gt;    &lt;li&gt;wl.phone_numbers: 讀取使用者私人、公司及行動電話號碼 &lt;/li&gt;    &lt;li&gt;wl.postal_addresses: 讀取使用者的郵寄地址 &lt;/li&gt;    &lt;li&gt;wl.share: 允許更新使用者的狀態 &lt;/li&gt;    &lt;li&gt;wl.skydrive: 讀取使用者的SkyDrive &lt;/li&gt;    &lt;li&gt;wl.skydrive_update: 讀取及更新使用者的SkyDrive &lt;/li&gt;    &lt;li&gt;wl.contacts_skydrive: 讀取連絡人(含使用者本人)的SkyDrive檔案 &lt;/li&gt;    &lt;li&gt;wl.work_profile: 取得使用者的僱主及工作位置資訊 &lt;/li&gt;    &lt;li&gt;wl.applications: 取得使用者的Client ID &lt;/li&gt;    &lt;li&gt;wl.applications_create: 建立Client ID &lt;/li&gt; &lt;/ul&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8673" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Live SDK" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Live+SDK/default.aspx" /></entry><entry><title>【茶包射手日記】JavaScript Date.getYear()傳回111</title><link rel="alternate" type="text/html" href="http://blog2.darkthread.net/post-2011-12-13-js-getyear-in-ie.aspx" /><id>http://blog2.darkthread.net/post-2011-12-13-js-getyear-in-ie.aspx</id><published>2011-12-13T10:11:00Z</published><updated>2011-12-13T10:11:00Z</updated><content type="html">&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;接獲使用者回報，某個透過JavaScript取日期的網頁在某些機器上產生的年份有誤，得到&amp;quot;111&amp;quot;的詭異結果!&lt;/p&gt;  &lt;p&gt;追蹤程式，發現它是用new Date().getYear()方法取年，而不是大家慣用的getFullYear()。而我們也很幸運地在一台Windows 7 + IE9完成問題重現，getYear()得到111，getFullYear()得到2011:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8668/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;有趣的是，此時找了另一台Win7 + IE9測試getYear()還真的傳回2011，順利地證實&amp;quot;有些IE9正常取得2011，有些IE9卻傳回111&amp;quot;的現象確實存在。&lt;/p&gt;  &lt;p&gt;爬文找到MDN &lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getYear"&gt;getYear()文件&lt;/a&gt;，才發現是自已孤陋寡聞:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;getYear()原本用來傳回兩位數的年份，想當然爾肯定有Y2K問題，故早已不再建議使用。(是誰寫進程式裡的，踹供!) &lt;/li&gt;    &lt;li&gt;為了向前相容，至今getYear()仍然繼續可用，針對超過2000的年份，會傳回getFullYear() - 1900，所以2011 - 1900 = 111，這就是111的由來。 &lt;/li&gt;    &lt;li&gt;如果是1900以前，則傳回負數，例如: 1895 - 1900 = -5。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;換句話說，依此原則傳回111才是對的，傳回2011反而是錯的(跟原本認知相反)。而看完Mozilla官方文件，我心中對於&amp;quot;同是IE9，行為卻不同&amp;quot;也有了答案。推測是努力向HTML業界標準看齊的IE，終於從IE9起洗心革面改掉getYear()行為，以求與眾家瀏覽器一致! &lt;/p&gt;  &lt;p&gt;把IE9的文件模式改成IE8再試一次，果然就傳回2011(如下圖)，證實我的推論: 之所以在不同IE9上得到不同結果，乃源於文件模式不同，&lt;font color="#f79646"&gt;在IE9+標準文件模式下，getYear()才會得到111；當IE9開啟相容模式(或使用IE7, IE8)時，則會得到2011&lt;/font&gt;。&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/8669/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;最後補充&lt;a href="http://msdn.microsoft.com/en-us/library/windows/apps/x0a9sc10%28v=vs.85%29.aspx"&gt;MSDN文件&lt;/a&gt;作為呈堂證供: (很有趣的一點，遠古時代的IE3跟IE9的行為是一致的，都是getFullYear() - 1900；而IE4到IE8，則是1900至1999傳回兩位數，範圍之外傳回四位數)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;In Internet Explorer 3.0, and then in Internet Explorer versions starting with Internet Explorer 9 standards mode, the value returned is the stored year minus 1900. For example, the year 1899 is returned as -1 and the year 2000 is returned as 100.&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;In Internet Explorer 4.0 through Internet Explorer 8 standards mode, the formula depends on the year. For the years 1900 through 1999, the value returned is a 2-digit value that is the stored year minus 1900. For dates outside that range, the 4-digit year is returned. For example, 1996 is returned as 96, but 1825 and 2025 are returned as is.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;另外的結論是: 將來誰再敢寫.getYear()，一律阿魯巴伺侯!!&lt;/p&gt;&lt;img src="http://blog2.darkthread.net/aggbug.aspx?PostID=8670" width="1" height="1"&gt;</content><author><name>Jeffrey</name><uri>http://blog2.darkthread.net/members/Jeffrey.aspx</uri></author><category term="Javascript" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Javascript/default.aspx" /><category term="Trouble-Shooting" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx" /><category term="IE" scheme="http://blog2.darkthread.net/blogs/darkthreadtw/archive/tags/IE/default.aspx" /></entry></feed>
