vb.net 服务器使用标准 asp.net mvc 发送事件(事件源)导致错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35631938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 19:44:48  来源:igfitidea点击:

server sent events (eventsource) with standard asp.net mvc causing error

asp.net-mvcvb.netserver-sent-eventseventsource

提问by Yisroel M. Olewski

I am trying my hand at server-sent events, but I cannot get it to work within an MVC project (not WebAPI). I haven't found any good samples online.

我正在尝试处理服务器发送的事件,但我无法让它在 MVC 项目(不是 WebAPI)中工作。我在网上没有找到任何好的样品。

This is the server-side code I tried (including several failed attempts from various posts):

这是我尝试过的服务器端代码(包括来自各种帖子的几次失败尝试):

Function GetRows() as ActionResult
    Dim ret = New HttpResponseMessage
    ' ret.Content.w
    ' Return ret

    Response.ContentType = "text/event-stream"
    Response.Write("data: " & "aaaa")
    Dim flag = False
    If flag Then
        For i = 0 To 100
            Response.Write("data: " & i)
        Next
    End If
    Response.Flush()
    'Return Content("Abv")
    'Return Content(("data: {0}\n\n" & DateTime.Now.ToString(), "text/event-stream")
End Function

and here is the Javascript

这是 Javascript

var source = new EventSource("/Tool/GetRows");
source.onmessage = function (event) {
    document.getElementById("messages").innerHTML += event.data + "<br>";
};
source.onerror = function (e) {
    console.log(e);
};

For some reason it's always going into onerror, and there is no information there what type of error it might be.

出于某种原因,它总是进入onerror,并且那里没有信息它可能是什么类型的错误。

What am I doing wrong?

我究竟做错了什么?

BTW, I don't think this action should really return anything, since my understanding is it should only be writing to the stream string by string.

顺便说一句,我不认为这个动作真的应该返回任何东西,因为我的理解是它应该只写一个字符串一个字符串的流。

回答by Zev Spitz

EventSource expects a specific format, and will raise onerrorif the stream doesn't match that format -- a set of lines of field/value pairs separated by a :, with each line ending in a newline character:

EventSource需要特定的 formatonerror如果流不匹配该格式,则将引发- 一组由 a 分隔的字段/值对:行,每行以换行符结尾:

field: value
field: value

fieldcan be one of data,event,id,retry, or empty for a comment (will be ignored by EventSource).

field可以是一个dataeventidretry,或空的注释(将被忽略的EventSource)。

datacan span multiple lines, but each of those lines must start with data:

data可以跨越多行,但每一行都必须以 data:

Each event trigger has to end with a double newline

每个事件触发器都必须以双换行符结尾

data: 1
data: second line of message

data: 2
data: second line of second message

Note: If you are writing in VB.NET, you can't use the \nescape sequence to write newlines; you have to use vbLfor Chr(10).

注意:如果是在VB.NET中编写,则不能使用\n转义序列来编写换行符;你必须使用vbLfChr(10)



As an aside, EventSource is supposed to hold an open connection to the server. From MDN(emphasis mine):

顺便说一句,EventSource 应该保持与服务器的开放连接。来自MDN(强调我的):

The EventSourceinterface is used to receive server-sent events. It connects to a server over HTTP and receives events in text/event-stream format without closing the connection.

EventSource的接口用于接收服务器发送的事件。它通过 HTTP 连接到服务器并在不关闭连接的情况下以文本/事件流格式接收事件。

Once control exits from an MVC controller method, the result will be packaged up and sent to the client, and the connection will be closed. Part of EventSource is that the client will try reopening the connection, which will once again be immediately closed by the server; the resulting close -> reopencycle can also be seen here.

一旦控制退出 MVC 控制器方法,结果将被打包并发送到客户端,并且连接将被关闭。EventSource 的一部分是客户端会尝试重新打开连接,这会再次被服务器立即关闭;由此产生的close -> reopen循环也可以在这里看到。

Instead of exiting from the method, the method should have some sort of loop that will be continuously writing to the Responsestream.

该方法不应退出该方法,而是应该具有某种循环,该循环将不断写入Response流。



Example in VB.NET

VB.NET 中的示例

Imports System.Threading
Public Class HomeController
    Inherits Controller

    Sub Message()
        Response.ContentType= "text/event-stream"
        Dim i As Integer
        Do
            i += 1
            Response.Write("data: DateTime = " & Now & vbLf)
            Response.Write("data: Iteration = " & i & vbLf)
            Response.Write(vbLf)
            Response.Flush

            'The timing of data sent to the client is determined by the Sleep interval (and latency)
            Thread.Sleep(1000) 
        Loop
    End Sub
End Class


Example in C#

C# 中的示例

Client-side:

客户端:

<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />

<script>
    var es = new EventSource('/home/message');
    es.onmessage = function (e) {
        console.log(e.data);
    };
    es.onerror = function () {
        console.log(arguments);
    };

    $(function () {
        $('#ping').on('click', function () {
            $.post('/home/ping', {
                UserID: $('#userid').val() || 0
            });
        });
    });
</script>

Server-side:

服务器端:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace EventSourceTest2.Controllers {
    public class PingData {
        public int UserID { get; set; }
        public DateTime Date { get; set; } = DateTime.Now;
    }

    public class HomeController : Controller {
        public ActionResult Index() {
            return View();
        }

        static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();

        public void Ping(int userID) {
            pings.Enqueue(new PingData { UserID = userID });
        }

        public void Message() {
            Response.ContentType = "text/event-stream";
            do {
                PingData nextPing;
                if (pings.TryDequeue(out nextPing)) {
                    Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
                }
                Response.Flush();
                Thread.Sleep(1000);
            } while (true);
        }
    }
}