注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

那些星星点点的微芒,终会成为燃烧生命的熊熊之光

 
 
 
 
 

日志

 
 

WebResource.axd 原理与问题  

2012-08-31 14:43:48|  分类: 安全一路走来 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

本文英文原版及代码下载:
http://aspnet.4guysfromrolla.com/articles/080906-1.aspx

利用WebResource.axd通过一个URL来访问装配件的内置资源

导言:

很多ASP.NET server控件都需要另外的外部资源来实现某些功能.比如,使用任何一个ASP.NET validation验证控件时,都需要一系列的JavaScript functions来执行它们的客户端验证.虽然可以在页面上添加这些JavaScript functions,不过更高效的方法是将这些函数封装在一个外部的JavaScript文件里,然后在页面通过<script src="PathToExternalJavaScriptFile" type="text/javascript" >的形式来将该文件包含在页面里.这样一来不仅可以实现对页面的瘦身,还可以允许浏览器对该JavaScript文件施行缓存(这样就不用每个页面在登录/回传时向浏览器发送该JavaScript代码了)

在ASP.NET 2.0之前,用户浏览器要访问这种外部资源的话,我们必须将它们作为具体的文件放在文件系统里.如果你使用ASP.NET 1.x的验证控件的话,你的页面必须添加一个对JavaScript文件的引用,如/aspnet_client/system_web/version/WebUIValidation.js.这些外部文件有碍最后的部署.

为解决这个问题,ASP.NET 2.0允许将外部资源植入控件的装配件里,通过一个指定的URL对其访问.将外部images, JavaScript files,CSS files植入控件的装配件后,部署就容易了,因为所有的资源都包含在.dll文件里了. 完成植入操作后,在ASP.NET 2.0页面里我们可以通过一个指定的URL(WebResource.axd)来实现对这些资源的访问.

本文我们探讨如何将外部资源植入装配件以及如何通过一个指定的URL来对其访问,该技术简化了安装和部署.

一个Custom Control示例...

为了考察ASP.NET 2.0里的外部资源特性,我们需要构建一个包含外部资源的自定义控件.就本文而言,我将创建一个很简单的控件来对TextBox控件的基本函数进行扩充.该控件—FunkyTextBox,在其客户端的onkeypress event事件里,用一个color数组里定义的颜色随机的改变TextBox的底色.

该行为需要一些客户端脚本:首先,我们要添加一个函数来获取一个随机数,然后根据该随机数设置TextBox的背景颜色.第二,我们要定义要用到的color数组.我们可以轻而易举的绕开外部文件,直接向页面注入要用到的JavaScript.下面的代码显示了FunkyTextBox控件的完整代码(为简化起见,using声明、命名空间、以及定义好的colors数组都省略掉了)

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个方法.第一,在AddAttributesToRender方法里,添加了一个客户端特性(client-side attribute),那么任何时候,当用户在textbox控件里键入字符时,就会调用名为ChangeBackgroundColor的JavaScript function,传入对该textbox的引用.第二, 在FunkyTextBox的PreRender event事件里,通过 Page.ClientScript class's RegisterScriptBlock method方法直接将所需要的JavaScript注入到页面里.具体来说,定义了ChangeBackgroundColor function和funkyTextBoxColor数组,而ChangeBackgroundColor function获取一个随机值,再根据该值用funkyTextBoxColor数组里对应的颜色来设置背景色.

当把FunkyTextBox添加到一个ASP.NET页面后,最终页面将包含如下的代码:

<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" />

通过OnPreRender method方法将JavaScript注入到页面,而FunkyTextBox控件呈现为一个 <input type="text">标记,其onkeypress event事件调用ChangeBackgroundColor function函数.

将JavaScript转移到一个外部文件以对页面瘦身

该FunkyTextBox控件使用的JavaScript很简单,稍微复杂点的控件为实现其功能所需的JavaScript远不止这么多.直接将JavaScript植入页面将增大页面的size,也会让用户花更多的时间加载页面. 为了对页面瘦身,应将JavaScript转移到一个外部文件里,再在页面里添加一个<script>标签,以实现对JavaScript file的引用,比如:

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

用<script>元素来替换JavaScript就达到了对页面瘦身的目的.此外,如果浏览器对外部文件实施缓存的话,那么我们就只需要下载一次就可以了(如果直接将JavaScript植入页面的话,每个页面每次登陆/加载时都要进行加载)

在ASP.NET 1.x里,对这些外部资源文件——JavaScript文件、CSS classes, images等,都需要与custom control一起加载和展开.在ASP.NET 2.0里我们可以将这些外部资源植入该控件的装配件,再通过一个指定的URL实现对这些资源的访问.

将外部文件植入控件的Assembly

要将资源植入控件的装配件,我们需要在Visual Studio里把资源添加到你的server control project里(本文结尾处可下载的代码里,包含了FunkyTextBox Web control Project,以及一个test website).对FunkyTextBox控件来说,我添加了一个名为Funky.js的文件,它包含的JavaScript就是我们在前面的OnPreRender方法向页面注入的那个JavaScript..

完成添加后,在Solution Explorer里选中它并打开Properties窗口,将Build Action设置为"Embedded Resource", 如下图:

这样一来,一旦你生成解决方案时,该文件就会被植入最终的assembly里.

通过一个URL来访问Embedded Assembly


对这些内置资源,我们可以通过WebResource.axd HTTP Handler来进行访问。通过一个URL,比如http://yoursite/WebResource.axd?d=assemblyKey&t=dateTimeOfLastAssemblyWrite, 其中,assemblyKey是要访问的装配件的名称的加密处理后的string, 而dateTimeOfLastAssemblyWrite描述了该装配件最后一次被修改的日期.给定一个有效的assemblyKey,那么WebResource.axd HTTP Handler就可以返回内置资源的内容.

使用WebResource.axd HTTP Handler,我们要面临3个问题:

1.通过WebResource.axd HTTP Handler,使一个内置资源允许被访问.
2.向WebResource.axd HTTP Handler传递准确的querystring值,以便检索某个特定的内置资源
3.将第2步生成的URL放置在ASP.NET页面恰当的位置

默认情况下是不允许WebResource.axd HTTP Handler对装配件里的内置资源进行访问的,为重写该行为,我们必须向server control project的AssemblyInfo文件添加一个特性(attribute),具体来说,要添加一个WebResource特性,比如:

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

注意:如果你使用的是Visual Basic,那么你需要用“<”和“>”来限定装配件,比如<assembly: WebResource(...)>.要看该AssemblyInfo文件的话,在Solution Explorer里选"Show All Files",它出现在 "My Project"文件夹下;再者,WebResource特性位于System.Web.UI命名空间,因此我们要在AssemblyInfo.cs文件顶端添加using System.Web.UI声明(如果是VB的话,用Imports System.Web.UI).

其中webResource参数指定了要访问的资源的名称,其命名模式为:RootNamespace.PathToFile.对本文示例而言,根命名空间为FunkyTextBox;又因资源文件在工程的根目录下,那么PathToFile就是Funky.js(假如Funky.js位于根目录的Scripts文件夹,那么PathToFile值就变成了Scripts.Funky.js).因此,webResource参数的值就为FunkyTextBox.Funky.js.

而contentType参数指定了要检索的资源的MIME type.当查询一个外部资源时,浏览器单独向服务器发出一个HTTP request.而MIME type就告知浏览器要返回的数据的类型.对JavaScript文件而言,其对应的contentType类型为text/javascript,有关MIME Media Types的更多信息,请参阅官方的MIME types清单.

最后,performSubstitution是一个可选的布尔值,用于指明该资源是否可以被其它的外部资源引用.比如,你可能将image文件和一个JavaScript文件植入到了装配件里,而该脚本文件可能需要引用某些image资源.如果确实需要的话,就将performSubstitution参数设为True.

对本文用到的控件而言,我们用如下的attribute声明来对Funky.js外部文件注册:

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

对资源进行注册后,我们就可以用WebResource.axd HTTP Handler来对其进行访问了.剩下的工作就是写代码来生成该外部资源对应的相应URL.

为此,我们使用ClientScriptManager class类里的GetWebResourceUrl(type, webResource) method方法.其中type参数就是该控件的类型,

webResource参数就是前面的WebResource特性里使用的webResource的值. 比如,要获取一个从该server control里访问内置的Funky.js资源的URL,我这样实现:

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

另外,我们在FunkyTextBox控件的OnPreRender事件里,将RegisterClientScriptInclude 方法和GetWebResourceUrl方法搭配使用,将相应的JavaScript包含进来(<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);
}

通过这个重写的OnPreRender方法,控件将向页面添加如下的标记:

<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引进来了.WebResource.aspx HTTP Handler获取该内置资源并将其发送回客户端.

结语

本文我们考察了如何将资源植入一个自定义ASP.NET server control的装配件里,以及如何通过一个URL来访问这些资源.这是ASP.NET 2.0里的新技术,便于开发者将外部资源植入装配件.


如若遇到以下情况:

1)、所有涉及WebResource.axd引用的脚本不可使用;
2)、TreeView控件中图片不能显示,出现“X”号,当然这个也属于与WebResource.axd文件相关联;
3)、类似使用HyperLink、DropDownList等控件触发的“WebForm_PostBackOptions”未定义及下图中的WebForm_AutoFocus('xx')未找到对象等事件报错。

以上均是由WebResource.axd提供,此文件并非真实存在,它是ASP.NET2.0提供的Web Resources管理模型,用于管理images、CSS、js等外部资源,它属于IIS中的一个ISAPI映射。关于如何管理Web Resource资源请参考http://birdshome.cnblogs.com/archive/2004/12/19/79309.html 及http://blog.miniasp.com/?tag=/webresource.axd 这两篇文章。

.axd文件映射设置位于IIS主目录>应用程序设置>配置>应用程序扩展。

WebResource.axd后跟有参数,形如

<script src="/WebResource.axd?d=TVfbQ3jRv_bGyGJiNtAlIQ2&amp;t=634030634711406250" type="text/javascript"></script>

<img src="/WebResource.axd?d=NPU1tNcW9bhj2HGf2OPUXyRv__ALwmYPAmAw1DkERv01&amp;t=634030634711406250" alt="" />

这里d显示为directory,对应资源的路径;t为timestamp,这个参数是为了保证资源不被缓存(还记得验证码后的Math.random()吗?)。

因此出现此类与WebResource.axd相关的问题,原因一般就是IIS中未对此axd文件进行处理,导致相应的图片、js等资源文件无法输出,引发此类问题。解决方法是,首先就应当检查IIS中是否已经添加了axd的映射;可以手工添加,不过还是推荐先运行aspnet_regiis.exe -i进行安装,然后查看是否已添加,若没有再进行手工添加。注意这里一定要将“确认文件是否存在”的小勾勾取消选择,见上图。

Assembly与服务器时间的问题

是不是有时候按上面讲的做还是解决不了问题?那这里就很大可能是下面的这个原因了:你的服务器时间不对——肯定是比正常时间要早。

这种错误可以打开IE8的开发人员工具,打开脚本面板,选择其中的一个WebResource.axd文件,其中源文件会报如下错误:

[ArgumentOutOfRangeException: 指定的参数已超出有效值的范围。 参数名: utcDate] System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate)

这里大概是看出是哪个地方出现的原因了:是因为当前asp.net生成的dll程序集的最后修改时间+2316706小于当前时间,也就是说服务器时间比程序集生成时间早,导致无法使用程序集。

这是我的bin目录,生成时间是2010-3-14

而我的当前系统时间为2009年3月14日,整整早了一年啊。

关于这个程序集与服务器时间问题的更详细内容,可参考此文http://blog.brianhartsock.com/2009/02/21/systemargumentoutofrangeexception-at-systemwebhttpcachepolicyutcsetlastmodifieddatetime-utcdate/

  评论这张
 
阅读(595)| 评论(0)
推荐

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017