| 记得当ASP.NET 
                          AJAX还在ATLAS阶段的时候,我就有发过一篇关于脚本资源文件可以被压缩的blog Great!The 
                          Atlas client library has been compressed in release 
                          mode. 从ATLAS到正式的ASP.NET AJAX已经发生了根本性的变化了,所以当时的情况就不再多做介绍了.还是先来看看一组数据 
                           
                            | Resource Name | DEBUG Uncompressed | DEBUG  Compressed | Release Uncompressed | Release Compressed |   
                            | MicrosoftAjax.js | 260,705bytes | 44,512 bytes | 84,337 bytes | 23,765 bytes |   
                            | MicrosoftAjaxWebForms.js | 66,186 bytes | 11,718 bytes | 29,841 bytes | 7,839 bytes |  上表的数据我就不再多说了,一目了然。那么由谁来决定是使用Debug版本,还是Release版本。你可以设置ScriptManager的ScriptMode属性,也可以修改web.config的compilation节点的deubg属性。 现在我们再来讨论一下,该如何实现脚本的压缩的?其实,这个也是相当简单的,从ASP.NET AJAX给我们提供的默认配合节点中,可以非常明显的看到这样一段代码: <scriptResourceHandler enableCompression="true" 
                          enableCaching="true" />但是它是被注释着的,我们只需要把这个注释去掉就OK了,奇迹就这样发生了。
 但是,事实完全就是这样吗?未必!我想说的是,这里的压缩并不适用于低于IE7的浏览器,包括IE6。有人肯定不信了,IE6可是绝大多数据普通用户使用的浏览器,这个都不支持,那这个功能还有什么意义啊!您还真别不信,事实就是这样。我们先来看一段代码,然后再来看一个例子。 直接找到RuntimeScriptResourceHandler类,它实现了IScriptResourceHandler 
                          该接口只有一个方法GetScriptResourceUrl顾名思义它就是获取访问脚本资源的URL地址,在RuntimeScriptResourceHandler中,它的实现是这样的: string IScriptResourceHandler.GetScriptResourceUrl(Assembly assembly, string resourceName, CultureInfo culture, 
bool zip, bool notifyScriptLoaded)
{
    if (!ScriptResourceHandler.IsCompressionEnabled(HttpContext.Current))
    {
        zip = false;
    }
    Tuple tuple = new Tuple(new object[] { assembly, resourceName, culture, zip, notifyScriptLoaded });
    string text = (string) _urlCache[tuple];
    if (text == null)
    {
        string name;
        ScriptResourceHandler.ScriptResourceInfo instance = ScriptResourceHandler.ScriptResourceInfo.GetInstance(assembly, resourceName);
        if (instance == ScriptResourceHandler.ScriptResourceInfo.Empty)
        {
            ThrowUnknownResource(resourceName);
        }
        Stream manifestResourceStream = assembly.GetManifestResourceStream(instance.ScriptName);
        if ((manifestResourceStream == null) || (manifestResourceStream.ReadByte() == -1))
        {
            ThrowUnknownResource(resourceName);
        }
        culture = ScriptResourceHandler.DetermineNearestAvailableCulture(assembly, resourceName, culture);
        Pair<AssemblyName, DateTime> assemblyInfo = ScriptResourceHandler.GetAssemblyInfo(assembly);
        AssemblyName first = assemblyInfo.First;
        DateTime second = assemblyInfo.Second;
        if (assembly.GlobalAssemblyCache)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(first.Name);
            builder.Append(',');
            builder.Append(first.Version);
            builder.Append(',');
            if (first.CultureInfo != null)
            {
                builder.Append(first.CultureInfo);
            }
            builder.Append(',');
            builder.Append(HexParser.ToString(first.GetPublicKeyToken()));
            name = builder.ToString();
        }
        else
        {
            name = first.Name;
        }
        if (_absoluteScriptResourceUrl == null)
        {
            _absoluteScriptResourceUrl = VirtualPathUtility.ToAbsolute("~/ScriptResource.axd");
        }
        text = string.Concat(new object[] { _absoluteScriptResourceUrl, "?d=", ScriptResourceHandler.EncryptString((zip ?(notifyScriptLoaded ? "Z" : "z") : (notifyScriptLoaded ? "U" : "u")) + name + "|" + resourceName + "|" +
 culture.ToString()), "&t=", second.Ticks });
        _urlCache[tuple] = text;
    }
    return text;
}
                 CODE 1
其中,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 = HeaderUtility.IsEncodingInAcceptList(this.IPage.Request.Headers["Accept-encoding"], "gzip");
            this._zipSet = true;
        }
        return this._zip;
    }
}
                CODE 2
决定它值的是客户端HTTP请求头部是否带有Accept-Encoding: gzip。因此大部分的浏览器都支持GZIP压缩过的HTTP输出流程,因此大部份浏览器的请求头部都会有这么一段:Accept-Encoding: 
                          gzip, deflate。因此这个值在接受IE6请求时,应该是为true才对的,而它得到的应该也是个压缩版本的资源URL请求才对啊?除非CODE1中的zip参数被改为false了。再回过头来看CODE1的开始部分有这么一段代码:     if (!ScriptResourceHandler.IsCompressionEnabled(HttpContext.Current))
    {
        zip = false;
    }
                          CODE 3
只有在这里zip的值才有可能被修改为false,那我们再来看看,ScriptResourceHandler.IsCompressionEnabled究竟做了此什么了? private static bool IsCompressionEnabled(HttpContext context)
{
    if (!ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCompression)
    {
        return false;
    }
    if ((context != null) && context.Request.Browser.IsBrowser("IE"))
    {
        return (context.Request.Browser.MajorVersion > 6);
    }
    return true;
}
       CODE 4
第一,我们可以确定EnableCompression的值为true。第二,我们使用的是IE,会执行return 
                          (context.Request.Browser.MajorVersion > 6) ,因为我们使用的IE6,这边就会返回false。回到CODE 
                          3,zip的值就会被修改成false了。而此时就会返回不被压缩的URL地址了。问题就在这里,可这是为什么呢?我想一般情况下我们肯定会不理解的,看了这个你就清楚了:http://www.microsoft.com/downloads/details.aspx?familyid=85bb441a-5bb1-4a82-86ec-a249af287513&displaylang=en 
                          原来在IE6的SP1版本中,接收GZIP的数据会有问题,而这边就是给了解决这个问题的补丁包。ASP.NET 
                          AJAX团队,可能担心由于这个问题引起的部分IE浏览器无法正常使用ASP.NET AJAX,保险起见,在IE6的请求中永不使用压缩脚本。 分析了代码,为了让我们有更直观的印象,再来看一段代码例子:  protected void Page_Load(object sender, EventArgs e)
        {
            NameValueCollection queryString = HttpUtility.ParseQueryString(GetScriptResourceUrl());
            Response.Write(DecryptString(queryString[0]));
        }
        private static string DecryptString(string s)
        {
            MethodInfo _decryptString = typeof(Page).GetMethod("DecryptString", BindingFlags.NonPublic | BindingFlags.Static);
            return (string)_decryptString.Invoke(null, new object[] { s });
        }
        private string GetScriptResourceUrl()
        {
            MethodInfo GetScriptResourceUrl = typeof(ScriptManager).GetMethod("GetScriptResourceUrl", BindingFlags.NonPublic |BindingFlags.Instance);
            return (string)GetScriptResourceUrl.Invoke(sm, new object[] { "MicrosoftAjax.js", sm.GetType().Assembly });
        }
在IE6,它输出的是 USystem.Web.Extensions|MicrosoftAjax.js| 
                          ,而在FF中它输出的是:ZSystem.Web.Extensions|MicrosoftAjax.js| 这两个不同的输出值完全就可以体现了它们请求行为的不同。 接下来,简单讨论一下,我们如何修改让它去掉这个限制。在ScriptResourceHandler有一个这样的静态方法SetScriptResourceHandler,我们可以重新实现一个IScriptResourceHandler类,用这个法植入到ASP.NET 
                          AJAX内部,让它们使用。但问题是SetScriptResourceHandler是一个internal的方法,使用反射呗,那还能怎么样,谁让它这样设计,既提供这个方法,又不想让人用! |