利用WebResource.axd通過(guò)一個(gè)URL來(lái)訪問(wèn)裝配件的內(nèi)置資源

導(dǎo)言:

很多ASP.NET server控件都需要另外的外部資源來(lái)實(shí)現(xiàn)某些功能.比如,使用任何一個(gè)ASP.NET validation驗(yàn)證控件時(shí),都需要一系列的JavaScript functions來(lái)執(zhí)行它們的客戶端驗(yàn)證.雖然可以在頁(yè)面上添加這些JavaScript functions,不過(guò)更高效的方法是將這些函數(shù)封裝在一個(gè)外部的JavaScript文件里,然后在頁(yè)面通過(guò)<script src="PathToExternalJavaScriptFile" type="text/javascript" >的形式來(lái)將該文件包含在頁(yè)面里.這樣一來(lái)不僅可以實(shí)現(xiàn)對(duì)頁(yè)面的瘦身,還可以允許瀏覽器對(duì)該JavaScript文件施行緩存(這樣就不用每個(gè)頁(yè)面在登錄/回傳時(shí)向?yàn)g覽器發(fā)送該JavaScript代碼了)

在ASP.NET 2.0之前,用戶瀏覽器要訪問(wèn)這種外部資源的話,我們必須將它們作為具體的文件放在文件系統(tǒng)里.如果你使用ASP.NET 1.x的驗(yàn)證控件的話,你的頁(yè)面必須添加一個(gè)對(duì)JavaScript文件的引用,如/aspnet_client/system_web/version/WebUIValidation.js.這些外部文件有礙最后的部署.

為解決這個(gè)問(wèn)題,ASP.NET 2.0允許將外部資源植入控件的裝配件里,通過(guò)一個(gè)指定的URL對(duì)其訪問(wèn).將外部images, JavaScript files,CSS files植入控件的裝配件后,部署就容易了,因?yàn)樗械馁Y源都包含在.dll文件里了. 完成植入操作后,在ASP.NET 2.0頁(yè)面里我們可以通過(guò)一個(gè)指定的URL(WebResource.axd)來(lái)實(shí)現(xiàn)對(duì)這些資源的訪問(wèn).

本文我們探討如何將外部資源植入裝配件以及如何通過(guò)一個(gè)指定的URL來(lái)對(duì)其訪問(wèn),該技術(shù)簡(jiǎn)化了安裝和部署.

一個(gè)Custom Control示例...

為了考察ASP.NET 2.0里的外部資源特性,我們需要構(gòu)建一個(gè)包含外部資源的自定義控件.就本文而言,我將創(chuàng)建一個(gè)很簡(jiǎn)單的控件來(lái)對(duì)TextBox控件的基本函數(shù)進(jìn)行擴(kuò)充.該控件—FunkyTextBox,在其客戶端的onkeypress event事件里,用一個(gè)color數(shù)組里定義的顏色隨機(jī)的改變TextBox的底色.

該行為需要一些客戶端腳本:首先,我們要添加一個(gè)函數(shù)來(lái)獲取一個(gè)隨機(jī)數(shù),然后根據(jù)該隨機(jī)數(shù)設(shè)置TextBox的背景顏色.第二,我們要定義要用到的color數(shù)組.我們可以輕而易舉的繞開(kāi)外部文件,直接向頁(yè)面注入要用到的JavaScript.下面的代碼顯示了FunkyTextBox控件的完整代碼(為簡(jiǎn)化起見(jiàn),using聲明、命名空間、以及定義好的colors數(shù)組都省略掉了)

public class FunkyTextBox : TextBox
{
    protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
    {
        // Wire up the onkeypress event handler to the ChangeBackgroundColor() JavaScript function
        writer.AddAttribute("onkeypress", "ChangeBackgroundColor(this);");

        base.AddAttributesToRender(writer);
    }


    protected override void OnPreRender(EventArgs e)
    {
        // Dump in the JavaScript directly into the page
        // (Although the external JavaScript approach is more efficient)
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "FunkyTextBox",
@"
function ChangeBackgroundColor(ctrl)
{
  var rndNum = Math.floor(Math.random() * 256);
  if (ctrl && funkyTextBoxColors.length > rndNum)
    ctrl.style.backgroundColor = funkyTextBoxColors[rndNum];
}

var funkyTextBoxColors =  new Array('#89C142', '#EF14E5', '#75762C', '#BB9E24', '#BF4A44', '#9D4A77', ...);
", true);

        base.OnPreRender(e);
    }
}

該server control繼承自ASP.NET的TextBox,重寫了2個(gè)方法.第一,在AddAttributesToRender方法里,添加了一個(gè)客戶端特性(client-side attribute),那么任何時(shí)候,當(dāng)用戶在textbox控件里鍵入字符時(shí),就會(huì)調(diào)用名為ChangeBackgroundColor的JavaScript function,傳入對(duì)該textbox的引用.第二, 在FunkyTextBox的PreRender event事件里,通過(guò) Page.ClientScript class's RegisterScriptBlock method方法直接將所需要的JavaScript注入到頁(yè)面里.具體來(lái)說(shuō),定義了ChangeBackgroundColor function和funkyTextBoxColor數(shù)組,而ChangeBackgroundColor function獲取一個(gè)隨機(jī)值,再根據(jù)該值用funkyTextBoxColor數(shù)組里對(duì)應(yīng)的顏色來(lái)設(shè)置背景色.

當(dāng)把FunkyTextBox添加到一個(gè)ASP.NET頁(yè)面后,最終頁(yè)面將包含如下的代碼:

<script type="text/javascript">
<!--

function ChangeBackgroundColor(ctrl)
{
    var rndNum = Math.floor(Math.random() * 256);
    if (ctrl && funkyTextBoxColors.length > rndNum)
        ctrl.style.backgroundColor = funkyTextBoxColors[rndNum];
}

var funkyTextBoxColors =  new Array('#89C142', '#EF14E5', '#75762C', '#BB9E24', '#BF4A44', '#9D4A77', ...);
// -->
</script>


Here is a FunkyTextBox:
<input onkeypress="ChangeBackgroundColor(this);" name="MyTextBox" type="text" id="MyTextBox" />

通過(guò)OnPreRender method方法將JavaScript注入到頁(yè)面,而FunkyTextBox控件呈現(xiàn)為一個(gè) <input type="text">標(biāo)記,其onkeypress event事件調(diào)用ChangeBackgroundColor function函數(shù).

將JavaScript轉(zhuǎn)移到一個(gè)外部文件以對(duì)頁(yè)面瘦身

該FunkyTextBox控件使用的JavaScript很簡(jiǎn)單,稍微復(fù)雜點(diǎn)的控件為實(shí)現(xiàn)其功能所需的JavaScript遠(yuǎn)不止這么多.直接將JavaScript植入頁(yè)面將增大頁(yè)面的size,也會(huì)讓用戶花更多的時(shí)間加載頁(yè)面. 為了對(duì)頁(yè)面瘦身,應(yīng)將JavaScript轉(zhuǎn)移到一個(gè)外部文件里,再在頁(yè)面里添加一個(gè)<script>標(biāo)簽,以實(shí)現(xiàn)對(duì)JavaScript file的引用,比如:

<script src="PathToExternalJavaScriptFile" type="text/javascript" >

用<script>元素來(lái)替換JavaScript就達(dá)到了對(duì)頁(yè)面瘦身的目的.此外,如果瀏覽器對(duì)外部文件實(shí)施緩存的話,那么我們就只需要下載一次就可以了(如果直接將JavaScript植入頁(yè)面的話,每個(gè)頁(yè)面每次登陸/加載時(shí)都要進(jìn)行加載)

在ASP.NET 1.x里,對(duì)這些外部資源文件——JavaScript文件、CSS classes, images等,都需要與custom control一起加載和展開(kāi).在ASP.NET 2.0里我們可以將這些外部資源植入該控件的裝配件,再通過(guò)一個(gè)指定的URL實(shí)現(xiàn)對(duì)這些資源的訪問(wèn).

將外部文件植入控件的Assembly

要將資源植入控件的裝配件,我們需要在Visual Studio里把資源添加到你的server control project里(本文結(jié)尾處可下載的代碼里,包含了FunkyTextBox Web control Project,以及一個(gè)test website).對(duì)FunkyTextBox控件來(lái)說(shuō),我添加了一個(gè)名為Funky.js的文件,它包含的JavaScript就是我們?cè)谇懊娴腛nPreRender方法向頁(yè)面注入的那個(gè)JavaScript..

完成添加后,在Solution Explorer里選中它并打開(kāi)Properties窗口,將Build Action設(shè)置為"Embedded Resource", 如下圖:

這樣一來(lái),一旦你生成解決方案時(shí),該文件就會(huì)被植入最終的assembly里.

通過(guò)一個(gè)URL來(lái)訪問(wèn)Embedded Assembly


對(duì)這些內(nèi)置資源,我們可以通過(guò)WebResource.axd HTTP Handler來(lái)進(jìn)行訪問(wèn)。通過(guò)一個(gè)URL,比如http://yoursite/WebResource.axd?d=assemblyKey&t=dateTimeOfLastAssemblyWrite, 其中,assemblyKey是要訪問(wèn)的裝配件的名稱的加密處理后的string, 而dateTimeOfLastAssemblyWrite描述了該裝配件最后一次被修改的日期.給定一個(gè)有效的assemblyKey,那么WebResource.axd HTTP Handler就可以返回內(nèi)置資源的內(nèi)容.

使用WebResource.axd HTTP Handler,我們要面臨3個(gè)問(wèn)題:

1.通過(guò)WebResource.axd HTTP Handler,使一個(gè)內(nèi)置資源允許被訪問(wèn).
2.向WebResource.axd HTTP Handler傳遞準(zhǔn)確的querystring值,以便檢索某個(gè)特定的內(nèi)置資源
3.將第2步生成的URL放置在ASP.NET頁(yè)面恰當(dāng)?shù)奈恢?/p>

默認(rèn)情況下是不允許WebResource.axd HTTP Handler對(duì)裝配件里的內(nèi)置資源進(jìn)行訪問(wèn)的,為重寫該行為,我們必須向server control project的AssemblyInfo文件添加一個(gè)特性(attribute),具體來(lái)說(shuō),要添加一個(gè)WebResource特性,比如:

[assembly: WebResource(webResource, contentType, performSubstitution)] 

注意:如果你使用的是Visual Basic,那么你需要用“<”和“>”來(lái)限定裝配件,比如<assembly: WebResource(...)>.要看該AssemblyInfo文件的話,在Solution Explorer里選"Show All Files",它出現(xiàn)在 "My Project"文件夾下;再者,WebResource特性位于System.Web.UI命名空間,因此我們要在AssemblyInfo.cs文件頂端添加using System.Web.UI聲明(如果是VB的話,用Imports System.Web.UI).

其中webResource參數(shù)指定了要訪問(wèn)的資源的名稱,其命名模式為:RootNamespace.PathToFile.對(duì)本文示例而言,根命名空間為FunkyTextBox;又因資源文件在工程的根目錄下,那么PathToFile就是Funky.js(假如Funky.js位于根目錄的Scripts文件夾,那么PathToFile值就變成了Scripts.Funky.js).因此,webResource參數(shù)的值就為FunkyTextBox.Funky.js.

而contentType參數(shù)指定了要檢索的資源的MIME type.當(dāng)查詢一個(gè)外部資源時(shí),瀏覽器單獨(dú)向服務(wù)器發(fā)出一個(gè)HTTP request.而MIME type就告知瀏覽器要返回的數(shù)據(jù)的類型.對(duì)JavaScript文件而言,其對(duì)應(yīng)的contentType類型為text/javascript,有關(guān)MIME Media Types的更多信息,請(qǐng)參閱官方的MIME types清單.

最后,performSubstitution是一個(gè)可選的布爾值,用于指明該資源是否可以被其它的外部資源引用.比如,你可能將image文件和一個(gè)JavaScript文件植入到了裝配件里,而該腳本文件可能需要引用某些image資源.如果確實(shí)需要的話,就將performSubstitution參數(shù)設(shè)為True.

對(duì)本文用到的控件而言,我們用如下的attribute聲明來(lái)對(duì)Funky.js外部文件注冊(cè):

[assembly: WebResource("FunkyTextBox.Funky.js", "text/javascript")]

對(duì)資源進(jìn)行注冊(cè)后,我們就可以用WebResource.axd HTTP Handler來(lái)對(duì)其進(jìn)行訪問(wèn)了.剩下的工作就是寫代碼來(lái)生成該外部資源對(duì)應(yīng)的相應(yīng)URL.

為此,我們使用ClientScriptManager class類里的GetWebResourceUrl(type, webResource) method方法.其中type參數(shù)就是該控件的類型,

webResource參數(shù)就是前面的WebResource特性里使用的webResource的值. 比如,要獲取一個(gè)從該server control里訪問(wèn)內(nèi)置的Funky.js資源的URL,我這樣實(shí)現(xiàn):

Page.ClientScript.GetWebResourceUrl(this.GetType(), "FunkyTextBox.Funky.js")

另外,我們?cè)贔unkyTextBox控件的OnPreRender事件里,將RegisterClientScriptInclude 方法和GetWebResourceUrl方法搭配使用,將相應(yīng)的JavaScript包含進(jìn)來(lái)(<script src="PathToExternalJavaScriptFile" type="text/javascript" >),如下:

protected override void OnPreRender(EventArgs e)
{
    // When pre-rendering, add in external JavaScript file
    Page.ClientScript.RegisterClientScriptInclude("FunkyJavaScript",
                 Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                             "FunkyTextBox.Funky.js"));

    base.OnPreRender(e);
}

通過(guò)這個(gè)重寫的OnPreRender方法,控件將向頁(yè)面添加如下的標(biāo)記:

<script src="/TestWebsite/WebResource.axd?d=NLu6bm6a2XinJZt4M-ujmQ13X5ALig6NEAZa1-AxV0HCbE3M-

VHNomDQt_qnxjdT0&t=632902136868023078" type="text/javascript"></script>

Here is a FunkyTextBox:
<input onkeypress="ChangeBackgroundColor(this);" name="MyTextBox" type="text" id="MyTextBox" />

這樣就將JavaScript引進(jìn)來(lái)了.WebResource.aspx HTTP Handler獲取該內(nèi)置資源并將其發(fā)送回客戶端.

結(jié)語(yǔ)

本文我們考察了如何將資源植入一個(gè)自定義ASP.NET server control的裝配件里,以及如何通過(guò)一個(gè)URL來(lái)訪問(wèn)這些資源.這是ASP.NET 2.0里的新技術(shù),便于開(kāi)發(fā)者將外部資源植入裝配件.