有没有一种方法可以覆盖或禁用ASP.NET WebForms客户端脚本注册?

4
ASP.NET WebForms的一个主要抱怨是框架从开发人员手中夺走了呈现文档的控制权——无数组件(既包括内置于ASP.NET中的,也包括第三方的)能够将它们自己的

你能否将名为 MyJQuery.1.4.1.js 的资源替换为 JQuery 1.6.2?保留名称,更新代码。 - John Wu
很遗憾,如果您使用与现有条目相同的键/名称进行后续调用RegisterClientScript,则会被忽略,无论脚本正文是否不同。 - Dai
我不是指要进行单独的调用。我的意思是去找到作为现有调用结果插入的资源,并将其替换为您想要的资源(较新版本的jquery)。 - John Wu
@JohnWu 这是我正在使用的库中的嵌入式资源 - 它具有强名称,因此如果我替换它,装配程序加载器会抱怨。 - Dai
使用绑定重定向。你肯定不能指望在不修改任何程序集的情况下解决这个问题! - John Wu
1个回答

4

这是我目前使用反射的解决方案 - 它不太美观,但现在它能工作:

public static class WebResourceOverrides
{
    private const BindingFlags PrivateThis = BindingFlags.NonPublic | BindingFlags.Instance;

    public static void OverrideClientScript(Page page, String key, String newContent)
    {
        if( page == null ) throw new ArgumentNullException(nameof(page));
        if( key == null ) throw new ArgumentNullException(nameof(key));
        if( newContent == null ) throw new ArgumentNullException(nameof(newContent));

        Type clientScriptType = page.ClientScript.GetType();

        FieldInfo clientScriptBlocksField           = clientScriptType.GetField( "_clientScriptBlocks", PrivateThis);
        FieldInfo registeredClientScriptBlocksField = clientScriptType.GetField( "_registeredClientScriptBlocks", PrivateThis );

        ArrayList      clientScriptBlocks           = (ArrayList)clientScriptBlocksField.GetValue( page.ClientScript );
        ListDictionary registeredClientScriptBlocks = (ListDictionary)registeredClientScriptBlocksField.GetValue( page.ClientScript );

        // clientScriptBlocks contains `System.Tuple<ScriptKey,String,Boolean>`, unfortunately `ScriptKey` is internal, so we can't play with the `Tuple` value directly.
        // ...but we can still access the fields:

        foreach(Object tuple in clientScriptBlocks)
        {
            Boolean found = ProcessScriptRegistration( tuple, key, newContent );
            if( found ) break;
        }
    }

    private static Boolean ProcessScriptRegistration(Object tuple, String key, String newContent)
    {
        Type tupleType = tuple.GetType();

        FieldInfo scriptKeyField = tupleType.GetField("m_Item1", PrivateThis );
        FieldInfo htmlField      = tupleType.GetField("m_Item2", PrivateThis); // Thankfully you can overwrite a `readonly` field using Reflection

        Object scriptKeyValue = scriptKeyField.GetValue( tuple );
        String html           = (String)htmlField.GetValue( tuple );

        Type scriptKeyType = scriptKeyValue.GetType();

        FieldInfo scriptKeyKeyField = scriptKeyType.GetField("_key", PrivateThis );
        String    scriptKeyKey      = (String)scriptKeyKeyField.GetValue( scriptKeyValue );

        if( scriptKeyKey == key )
        {
            htmlField.SetValue( tuple, newContent );
            return true;
        }

        return false;
    }
}

用法:

我从.master页面的OnPreRender方法中调用它,以确保如果页面使用了错误的jQuery脚本,则此时它将被添加到内部注册列表中(因为它通常是由库的控件的InitLoad事件处理程序添加的):

public class MyMasterPage : MasterPage {

    protected override void OnPreRender(EventArgs e) {

        base.OnPreRender( e );

        WebResourceOverrides.OverrideClientScript( this.Page, "jquery", "<!-- jQuery 1.4.1 disabled. -->" );
    }
}

这会导致页面的 <form> 渲染如下。原来通过 WebResource.axd 引用 jQuery1.4.1.js 文件的 <script> 块将被渲染,现在被替换为注释。
<form ...>
    <script src="/WebResource.axd?d=1234..."></script>
    <script src="/WebResource.axd?d=5678..."></script>
    <!-- jQuery 1.4.1 disabled. -->
    <script src="/WebResource.axd?d=90AB..."></script>

我的代码没有删除注册信息,因为客户端脚本注册的内部状态使用了一个整数索引的ArrayList,所以删除一个条目可能会破坏任何存储的索引 - 这意味着糟糕的库控件仍然认为它们的jQuery版本被引用了(因为它们中的一些调用了this.Page.ClientScript.IsClientScriptIncludeRegistered)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接