服务器使用标准 asp.net mvc 发送事件(事件源)导致错误
server sent events (eventsource) with standard asp.net mvc causing error
我正在尝试处理服务器发送的事件,但我无法让它在 MVC 项目(不是 WebAPI)中工作。我还没有在网上找到任何好的样本。
这是我试过的服务器端代码(包括来自不同帖子的几次失败尝试):
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
这里是 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);
};
出于某种原因,它总是进入 onerror
,并且没有任何信息表明它可能是什么类型的错误。
我做错了什么?
顺便说一句,我不认为这个动作应该 return 任何东西,因为我的理解是它应该只是逐个字符串地写入流。
EventSource expects a specific format,如果流与该格式不匹配,将引发 onerror
-- 由 :
分隔的一组 field/value 对行, 每行以换行符结尾:
field: value
field: value
field
可以是 data
、event
、id
、retry
之一,或者为空的注释(将被 EventSource 忽略) .
data
可以跨越多行,但每一行都必须以 data:
开头
每个事件触发器必须以双换行符结束
data: 1
data: second line of message
data: 2
data: second line of second message
注意:如果在VB.NET中书写,则不能使用\n
转义序列来写换行符;你必须使用 vbLf
或 Chr(10)
.
顺便说一句,EventSource 应该保持与服务器的开放连接。来自 MDN(强调我的):
The EventSource interface 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.
一旦控制从 MVC 控制器方法退出,结果将被打包并发送到客户端,连接将被关闭。 EventSource 的一部分是客户端将尝试重新打开连接,该连接将再次被服务器立即关闭;由此产生的 close -> reopen
循环也可以看到 here.
该方法不应退出该方法,而应具有某种循环,该循环将不断写入 Response
流。
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
C# 示例
客户端:
<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>
服务器端:
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);
}
}
}
我正在尝试处理服务器发送的事件,但我无法让它在 MVC 项目(不是 WebAPI)中工作。我还没有在网上找到任何好的样本。
这是我试过的服务器端代码(包括来自不同帖子的几次失败尝试):
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
这里是 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);
};
出于某种原因,它总是进入 onerror
,并且没有任何信息表明它可能是什么类型的错误。
我做错了什么?
顺便说一句,我不认为这个动作应该 return 任何东西,因为我的理解是它应该只是逐个字符串地写入流。
EventSource expects a specific format,如果流与该格式不匹配,将引发 onerror
-- 由 :
分隔的一组 field/value 对行, 每行以换行符结尾:
field: value
field: value
field
可以是 data
、event
、id
、retry
之一,或者为空的注释(将被 EventSource 忽略) .
data
可以跨越多行,但每一行都必须以 data:
每个事件触发器必须以双换行符结束
data: 1
data: second line of message
data: 2
data: second line of second message
注意:如果在VB.NET中书写,则不能使用\n
转义序列来写换行符;你必须使用 vbLf
或 Chr(10)
.
顺便说一句,EventSource 应该保持与服务器的开放连接。来自 MDN(强调我的):
The EventSource interface 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.
一旦控制从 MVC 控制器方法退出,结果将被打包并发送到客户端,连接将被关闭。 EventSource 的一部分是客户端将尝试重新打开连接,该连接将再次被服务器立即关闭;由此产生的 close -> reopen
循环也可以看到 here.
该方法不应退出该方法,而应具有某种循环,该循环将不断写入 Response
流。
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
C# 示例
客户端:
<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>
服务器端:
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);
}
}
}