关于Internet Explorer:通过SignalR在IE中缺少原型方法

Prototype methods missing in IE via SignalR

我遇到了一个问题,只在IE中处理原型方法消失(在本例中是array.prototype方法),而且只有当数组通过信号器时。

我写了一个小的/愚蠢的但简单的概念验证Web应用程序来演示这个问题(代码如下)。请注意,当您单击"更新所有客户机"然后单击"包含字母"r"的水果"时,"u"列表中的原型方法将丢失,从而导致异常。在这种情况下,阵列来自信号器。现在,当您单击"重置"并将数组重置为硬编码值时,"包含字母"r"的水果"按钮突然起作用——原型方法又回来了。记住,这个问题只发生在IE中。

提示:当我第一次写概念证明的时候,我不能复制这个问题。当数组通过信号器时,IE仍然有原型方法,但是当页面加载时,我确实有另一个错误。我无意中把jquery包括了两次。当我拿出多余的脚本来包含第二个jquery时,它修复了这个错误(显然),但现在问题可以重现了。然后,IE缺少了我创建的数组原型方法,但只有当数组通过信号器时。

myextensions.js(扩展名:myextensions.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Array.prototype.where = function (del)
{
    var ret = new Array();
    for (var i = 0; i < this.length; i++)
    {
        if (del(this[i])) ret.push(this[i]);
    }
    return ret;
}

Array.prototype.select = function (del)
{
    var ret = new Array();
    for (var i = 0; i < this.length; i++)
    {
        ret.push(del(this[i]));
    }
    return ret;
}

第四讲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        @ViewBag.Title - My ASP.NET MVC Application
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />

        @Styles.Render("~/Content/css")

        @Scripts.Render("~/Scripts/myExtensions.js")
        @Scripts.Render("~/bundles/modernizr")
        @Scripts.Render("~/Scripts/jquery-1.7.1.js")
        @Scripts.Render("~/Scripts/jquery.signalR-1.0.0-rc1.js")
        <script src="~/signalr/hubs">

    </head>
    <body>
        <header>
           
               
                    <p class="site-title">@Html.ActionLink("IE/SignalR error POC","Index","Home")
</p>
               
           
        </header>
       
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
       
        <footer>
           
               
                    <p>
&copy; @DateTime.Now.Year - My ASP.NET MVC Application
</p>
               
           
        </footer>
        @*@Scripts.Render("~/Scripts/myExtensions.js")*@
    </body>
</html>

速记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Linq;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalR_Bug_POC.Hubs
{
    public class ListHub : Hub
    {
        public void RunTest()
        {
            Clients.All.updateList(new string[]
                {
                   "apple","pear","grape","strawberry","rasberry","orange","watermelon"
                }.Select(f => new { Name = f }).ToList());
        }
    }
}

索引文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@{
    ViewBag.Title ="Home Page";
}

@if(false)
{
    @Scripts.Render("~/Scripts/jquery.signalR-1.0.0-rc1.js")
    @Scripts.Render("~/Scripts/myExtensions.js")
    <script src="~/signalr/hubs">
}

<script type="text/javascript">

    var _fruits = ["blueberry","grape","orange","strawberry"].select(function (f) { return {"Name": f } });
    var _list;

    var conn = $.connection.listHub;
    $.connection.hub.start();

    conn.client.updateList = function (data)
    {
        _list = data;
        $("#theList").html("");
        for (var i = 0; i < _list.length; i++)
        {
            $("#theList").append("
<li>
" + _list[i].Name +"
</li>
");
        }
    }

    $(document).ready(function ()
    {
        $("#cmdUpdateClients").click(function ()
        {
            conn.server.runTest();
        });
        $("#cmdReset").click(function ()
        {
            conn.client.updateList(_fruits);
        });
        $("#cmdRunTest").click(function ()
        {
            var message ="";
            var fruitsContaining = _list
                .where(function (f) { return f.Name.indexOf('r') >= 0 })
                .select(function (f) { return f.Name });
            for (var i = 0; i < fruitsContaining.length; i++)
            {
                message +=" -" + fruitsContaining[i] +"
";
            }
            alert(message);
        });
        conn.client.updateList(_fruits);
    });




<input type="button" id="cmdUpdateClients" value="Update All Clients" />
<input type="button" id="cmdReset" value="Reset" />
<input type="button" id="cmdRunTest" value="Fruits containing the letter r." />
<ul id="theList">
</ul>

我不确定是我在代码中做错了什么(也就是说,我做错了顺序),还是IE错误或信号错误。例如,当我在conn.client.updateList JS方法的第一行设置一个断点,并将调用堆栈跟踪到最顶端时,会发现即使在"data"对象中(在signal-receive方法中),数组也没有我的原型方法。


我遇到了同样的问题:当我使用信号器将数组从C_传递到角度应用程序时,我不能在接收到的对象上使用Array.prototype中定义的方法。此外,在这里描述的一些数组测试将失败的意义上,这些对象确实是"类似数组"的。例如,arr instanceof Array返回false,而Array.isArray(arr)返回true

当Web应用程序宿主在不支持WebSockets的IIS中时,问题就开始了。在这种情况下,信号器在chrome和firefox中默认为serversentevents,在Internet Explorer和edge中默认为foreverframe。

正如这个问题所指出的,ForeverFrame导致数组反序列化错误。这是因为foreverframe使用不同的帧来保持信号器连接,不同帧中的数组是使用不同的Array对象创建的。

这里有很多解决方案:

  • 如果可能,为IIS启用WebSockets。可以从IIS 8和Windows Server 2012开始执行此操作。
  • 如果websockets不可用,可以在$.connection.hub.start()参数中指定不应使用foreverframe,默认为ie和edge上的longpolling。
  • 您可以为foreverframe提供自己的JSON解析器,并调用window.JSON

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $.connection.hub.json = {
        parse: function(text, reviver) {
            console.log("Parsing JSON");
            return window.JSON.parse(text, reviver);
        },
        stringify: function(value, replacer, space) {
            return window.JSON.stringify(value, replacer, space);
        }
    };
  • 而且,正如皮特的回答中所建议的,你可以对接收到的对象调用Array.prototype.slice,将其转换为同一帧的Array。必须对从信号器接收到的任何阵列执行此操作,因此它不像其他两个选项那样具有可伸缩性。


  • 下面的修改对我来说很好:

    1
    2
    3
    var fruitsContaining = _list
        .where(function (f) { return f.indexOf('r') >= 0 })
        .select(function (f) { return f});

    Where和Select实际上在那里,它是f.name,而不是,因为数组成员是字符串。

    更新:

    好吧,忽略上面的内容。这里是修复:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var list = list
    if (navigator.appName == 'Microsoft Internet Explorer') {
        list = Array.prototype.slice.call(_list);
    }
    var fruitsContaining = list
        .where(function (f) { return f.Name.indexOf('r') >= 0 })
        .select(function (f) { return f.Name });
    for (var i = 0; i < fruitsContaining.length; i++) {
        message +=" -" + fruitsContaining[i] +"
    ";
    }

    我不完全理解这个问题,但我相信它可能是jquery中的一个bug。尽管这个问题有点不同,但我还是从这个问题中偷走了解决方案:为什么在我的jquery插件中,这个改变对数组原型不起作用?

    其他更新

    添加了IE检查。