asp.net

位置:IT落伍者 >> asp.net >> 浏览文章

ASP.NET AJAX 资源脚本压缩的秘密


发布日期:2018年05月13日
 
ASP.NET AJAX 资源脚本压缩的秘密
从ATLAS到正式的ASPNET AJAX已经发生了根本性的变化了所以当时的情况就不再多做介绍了还是先来看看一组数据 Resource NameDEBUG UncompressedDEBUG CompressedReleaseUncompressedRelease CompressedMicrosoftAjaxjsbytes bytes bytes bytesMicrosoftAjaxWebFormsjs bytes bytes bytes bytes

上表的数据我就不再多说了一目了然那么由谁来决定是使用Debug版本还是Release版本你可以设置ScriptManager的ScriptMode属性也可以修改nfig的compilation节点的deubg属性

现在我们再来讨论一下该如何实现脚本的压缩的?其实这个也是相当简单的从ASPNET AJAX给我们提供的默认配合节点中可以非常明显的看到这样一段代码

<scriptResourceHandler enableCompression=true enableCaching=true />

但是它是被注释着的我们只需要把这个注释去掉就OK了奇迹就这样发生了

但是事实完全就是这样吗?未必!我想说的是这里的压缩并不适用于低于IE的浏览器包括IE有人肯定不信了IE可是绝大多数据普通用户使用的浏览器这个都不支持那这个功能还有什么意义啊!您还真别不信事实就是这样我们先来看一段代码然后再来看一个例子

直接找到RuntimeScriptResourceHandler类它实现了IScriptResourceHandler 该接口只有一个方法GetScriptResourceUrl顾名思义它就是获取访问脚本资源的URL地址在RuntimeScriptResourceHandler中它的实现是这样的:

string IScriptResourceHandlerGetScriptResourceUrl(Assembly assembly string resourceName CultureInfo culture bool zip bool notifyScriptLoaded)

{

if (!ScriptResourceHandlerIsCompressionEnabled(HttpContextCurrent))

{

zip = false;

}

Tuple tuple = new Tuple(new object[]

{

assembly resourceName culture zip notifyScriptLoaded

}

);

string text = (string) _urlCache[tuple];

if (text == null)

{

string name;

ScriptResourceHandlerScriptResourceInfo instance = ScriptResourceHandlerScriptResourceInfoGetInstance(assembly resourceName);

if (instance == ScriptResourceHandlerScriptResourceInfoEmpty)

{

ThrowUnknownResource(resourceName);

}

Stream manifestResourceStream = assemblyGetManifestResourceStream(instanceScriptName);

if ((manifestResourceStream == null) || (manifestResourceStreamReadByte() == ))

{

ThrowUnknownResource(resourceName);

}

culture = ScriptResourceHandlerDetermineNearestAvailableCulture(assembly resourceName culture);

Pair<AssemblyName DateTime> assemblyInfo = ScriptResourceHandlerGetAssemblyInfo(assembly);

AssemblyName first = assemblyInfoFirst;

DateTime second = assemblyInfoSecond;

if (assemblyGlobalAssemblyCache)

{

StringBuilder builder = new StringBuilder();

builderAppend(firstName);

builderAppend();

builderAppend(firstVersion);

builderAppend();

if (firstCultureInfo != null)

{

builderAppend(firstCultureInfo);

}

builderAppend();

builderAppend(HexParserToString(firstGetPublicKeyToken()));

name = builderToString();

}

else

{

name = firstName;

}

if (_absoluteScriptResourceUrl == null)

{

_absoluteScriptResourceUrl = VirtualPathUtilityToAbsolute(~/ScriptResourceaxd);

}

text = stringConcat(new object[]

{

_absoluteScriptResourceUrl ?d= ScriptResourceHandlerEncryptString((zip ? (notifyScriptLoaded ? Z : z) : (notifyScriptLoaded ? U : u)) + name + | + resourceName + | + cultureToString()) &t= secondTicks

}

);

_urlCache[tuple] = text;

}

return text;

}

code

其中zip参数是用于指定是否生成带有压缩版本的URL地址如果zip为true则返回的参数d的第一个字符为Z或z否则为U或u具体是如何去压缩的我们现在先不管反正URL地址中的地址栏参数d的第一字符为Z或z就表明访问的资源被请求到客户端前会被压缩因此要访问压缩的脚本资源就要保证zip参数为true那这个参数从何而来呢?它是由ScriptManager的Zip属性原原本本的被传递到这个方法中这在传递的过程当中没有被修改过而ScriptManager的Zip的属性定义如下


internal bool Zip{

get

{

if (!this_zipSet)

{

this_zip = HeaderUtilityIsEncodingInAcceptList(thisIPageRequestHeaders[Acceptencoding] gzip);

this_zipSet = true;

}

return this_zip;

}

}

code

决定它值的是客户端HTTP请求头部是否带有AcceptEncoding: gzip因此大部分的浏览器都支持GZIP压缩过的HTTP输出流程因此大部份浏览器的请求头部都会有这么一段AcceptEncoding: gzip deflate因此这个值在接受IE请求时应该是为true才对的而它得到的应该也是个压缩版本的资源URL请求才对啊?除非CODE中的zip参数被改为false了再回过头来看CODE的开始部分有这么一段代码:

if (!ScriptResourceHandlerIsCompressionEnabled(HttpContextCurrent))

{

zip = false;

}

code

只有在这里zip的值才有可能被修改为false那我们再来看看ScriptResourceHandlerIsCompressionEnabled究竟做了此什么了?

private static bool IsCompressionEnabled(HttpContext context)

{

if (!ScriptingScriptResourceHandlerSectionApplicationSettingsEnableCompression)

{

return false;

}

if ((context != null) && contextRequestBrowserIsBrowser(IE))

{

return (contextRequestBrowserMajorVersion > );

}

return true;

}

code

第一我们可以确定EnableCompression的值为true第二我们使用的是IE会执行return (contextRequestBrowserMajorVersion > ) 因为我们使用的IE这边就会返回false回到CODE zip的值就会被修改成false了而此时就会返回不被压缩的URL地址了问题就在这里可这是为什么呢?我想一般情况下我们肯定会不理解的看了这个你就清楚了x?familyid=bbabbaecaaf&displaylang=en原来在IE的SP版本中接收GZIP的数据会有问题而这边就是给了解决这个问题的补丁包ASPNET AJAX团队可能担心由于这个问题引起的部分IE浏览器无法正常使用ASPNET ajax保险起见在IE的请求中永不使用压缩脚本

分析了代码为了让我们有更直观的印象再来看一段代码例子

protected void Page_Load(object sender EventArgs e)

{

NameValueCollection queryString = HttpUtilityParseQueryString(GetScriptResourceUrl());

ResponseWrite(DecryptString(queryString[]));

}

private static string DecryptString(string s)

{

MethodInfo _decryptString = typeof(Page)GetMethod(DecryptString BindingFlagsNonPublic | BindingFlagsStatic);

return (string)_decryptStringInvoke(null new object[] { s });

}

private string GetScriptResourceUrl()

{

MethodInfo GetScriptResourceUrl = typeof(ScriptManager)GetMethod(GetScriptResourceUrl BindingFlagsNonPublic | BindingFlagsInstance);

return (string)GetScriptResourceUrlInvoke(sm new object[]

{

MicrosoftAjaxjs smGetType()Assembly

}

);

}

在IE它输出的是 USystemWebExtensions|MicrosoftAjaxjs| 而在FF中它输出的是ZSystemWebExtensions|MicrosoftAjaxjs|

这两个不同的输出值完全就可以体现了它们请求行为的不同

接下来简单讨论一下我们如何修改让它去掉这个限制在ScriptResourceHandler有一个这样的静态方法SetScriptResourceHandler我们可以重新实现一个IScriptResourceHandler类用这个法植入到ASPNET AJAX内部让它们使用但问题是SetScriptResourceHandler是一个internal的方法使用反射呗那还能怎么样谁让它这样设计既提供这个方法又不想让人用!

OK对这个问题的研究就先到这边了

               

上一篇:ASP.NET页面间的传值的几种方法

下一篇:用ASP.NET设计高效邮件列表