5

我遇到了处理原型方法消失的问题(在本例中为 Array.prototype 方法),仅在 IE 中并且仅当数组通过 SignalR 时才消失。

我写了一个小/愚蠢但简单的概念验证网络应用程序来演示这个问题(代码都在下面)。请注意,当您单击“更新所有客户端”然后单击“包含字母 'r' 的水果”时,_list 中的原型方法丢失导致异常。在这种情况下,数组来自 SignalR。现在,当您单击“重置”并将数组重置为硬编码值时,“包含字母'r'的水果”按钮突然起作用-原型方法又回来了。请记住,这个问题只发生在 IE 中。

提示:当我第一次编写概念证明时,我无法重现该问题。当数组通过 SignalR 来时,IE 仍然有原型方法,但是当页面加载时我确实遇到了另一个错误。我不小心包含了 jQuery 两次。当我取出冗余脚本以包含第二个 jQuery 时,它修复了该错误(显然),但现在可以重现该问题。然后,IE 缺少我创建的数组原型方法,但只有当数组来自 SignalR 时。

myExtensions.js:

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;
}

_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <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"></script>

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

ListHub.cs

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());
        }
    }
}

索引.cshtml

@{
    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>
}

<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] + "\n";
            }
            alert(message);
        });
        conn.client.updateList(_fruits);
    });


</script>

<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 错误或 SignalR 错误。例如,当我在 conn.client.updateList JS 方法的第一行设置断点并跟踪调用堆栈到最顶部时,即使在“数据”对象中的数组(在 SignalR 接收方法中)也没有没有我的原型方法。

4

2 回答 2

3

我遇到了同样的问题:当我使用 SignalR 将数组从 C# 传递到 Angular 应用程序时,我无法使用Array.prototype在接收到的对象上定义的方法。此外,这些对象确实是“类似数组的”,因为这里描述的一些数组测试会失败。例如,arr instanceof Array会返回false,但Array.isArray(arr)会返回true

当 Web 应用程序托管在没有 WebSockets 支持的 IIS 中时,问题就开始了。在这种情况下,SignalR在 Chrome 和 Firefox 中默认为 serverSentEvents,在 Internet Explorer 和 Edge 中默认为 ForeverFrame。

正如这个问题所表明的,ForeverFrame 导致数组被错误地反序列化。这是因为 ForeverFrame 使用不同的帧来维护 SignalR 连接,并且不同帧中的数组是使用不同的Array对象创建的。

这里有很多解决方案:

  1. 如果可能,请为 IIS 启用 WebSockets。这可以从 IIS 8 和 Windows Server 2012 开始。
  2. 如果 WebSockets 不可用,可以$.connection.hub.start()参数中指定 ForeverFrame 不应该使用,IE 和 Edge 上默认为 LongPolling。
  3. 您可以将自己的 JSON 解析器提供给 ForeverFrame,并调用window.JSON

    $.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);
        }
    };
    
  4. 而且,正如皮特的回答所建议的那样,您可以调用Array.prototype.slice接收到的对象,将其转换为Array同一帧的。对于从 SignalR 接收到的任何数组都必须这样做,因此它不像其他两个选项那样可扩展。

于 2016-05-03T06:48:32.973 回答
0

通过以下修改,它对我来说很好:

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

where 和 select 实际上在那里,它是 f.Name 不是,因为数组成员是字符串。

更新:

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

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] + "\n";
}

我不完全理解这个问题,但我相信它可能是 jquery 中的一个错误。虽然问题有点不同,但我从这个问题中窃取了解决方案: 为什么对 Array 原型的这种更改在我的 jQuery 插件中不起作用?

额外更新

添加了 IE 检查。

于 2012-12-28T14:10:22.330 回答