我正在使用Html5服务器发送的事件。服务器端是Java Servlet。我有一个json数组数据要传递给服务器。
var source = new EventSource("../GetPointVal?id=100&jsondata=" + JSON.stringify(data));
如果数组大小较小,则服务器端可以获取querystring。但是如果数组大小很大。(可能超过数千个字符),服务器无法获取查询字符串。是否可以使用new EventSource(...)
中的POST方法将json数组传递给服务器,从而避免查询字符串长度限制?
否,SSE标准不允许POST。
(据我所知,没有技术原因——我认为这只是设计者从未见过用例:这不仅仅是大数据,但如果你想做一个自定义的身份验证方案,有安全原因不将密码放在GET数据中。)
XMLHttpRequest
(即AJAX)确实允许POST,因此一种选择是返回到旧的长轮询/彗星方法。(我的书《HTML5SSE的数据推送应用程序》详细介绍了如何做到这一点。)
另一种方法是预先将所有数据POST
存储在HttpSession
中,然后调用SSE进程,后者可以利用该会话数据。(SSE确实支持cookie,因此JSESSIONID
cookie应该可以正常工作。)
附言:标准没有明确规定不能使用POST。但是,与XMLHttpRequest
不同,没有参数可以指定要使用的http方法,也没有方法指定要发布的数据。
虽然您无法使用EventSource
API来执行此操作,但服务器无法实现POST请求的技术原因并不存在。诀窍是让客户端发送请求。例如,这个答案讨论了将sse.js作为EventSource
的替代品。
或者,您可以从使用另一个php 自定义的文件中读取数据
http://..../command_receiver.php?command=blablabla
command_receiver.php
<?php
$cmd = $_GET['command'];
$fh = fopen("command.txt","w");
fwrite($fh, $cmd);
fclose($fh);
?>
demo2_sse.php
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
//$a = $_GET["what"];
$time = microtime(true); //date('r');
$fa = fopen("command.txt", "r");
$content = fread($fa,filesize("command.txt"));
fclose($fa);
echo "data: [{$content}][{$time}]nn";
flush();
?>
EventSource包含在任意命名的html中,如下所示
<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
var source = new EventSource("demo2_sse.php");
source.onmessage = function (event) {
mycommand = event.data.substring(1, event.data.indexOf("]"));
mytime = event.data.substring(event.data.lastIndexOf("[") + 1, event.data.lastIndexOf("]"));
}
</script>
</body>
</html>
对于那些希望将此问题限制为一个请求的人,我有一种解决此问题的新方法。事件流请求将cookie传递到服务器,与任何AJAX请求相同。我的方法是在发出请求之前设置包含数据的cookie,然后在第一次响应时立即丢弃cookie。
现在,Cookie有一些限制需要注意。首先,cookie的最大长度为4096字节,包括名称以及可能的3字节开销。其次,每个浏览器的cookie安全数量为50。不同的浏览器有不同的最大数量,从谷歌的180到安卓的50。
我将由您决定如何围绕这些限制实现逻辑,但我将提供一个实现示例。请注意,此实现使用js cookie来操作cookie。我们还在服务器端使用cookie解析器中间件。
浏览器JS
const productIDs = ["b0708d2c-fe46-4251-96e0-1cfb3cd05eb0", "244d1e73-b5b4-4c59-8d4e-006fc1b190fe"];
const size = new TextEncoder().encode(JSON.stringify(productIDs)).length;
const chunkSize = Math.floor(1024 * 3.5);
const segments = Math.ceil(size / chunkSize);
const currentCookies = Object.keys(Cookies.get()).length;
if (segments + currentCookies <= 50){
let payloads = [];
for (let segment = 0; segment < segments; segment++){
const payloadLength = Math.ceil(productIDs.length / segments);
const startPosition = segment * payloadLength;
payloads[segment] = productIDs.slice(startPosition, startPosition + payloadLength);
}
for (const [index, payload] of Object.entries(payloads)){
Cookies.set(`qp[${index}]`, JSON.stringify(payload));
}
const source = new EventSource("start-processing");
source.addEventListener("connected", () => {
for (const [index, payload] of Object.entries(payloads)){
Cookies.remove(`qp[${index}]`);
}
});
}else{
console.log(`ERROR: Cookie limit was reached.`);
}
如您所见,我们将chunkSize
设置为3584字节。这看起来是一个相当安全的填充量,但你可以根据自己的需要进行调整。请记住,此方法将在所有有效负载数组中均匀分布数组值。这意味着您不太可能完全达到chunkSize值。
您可以将cookie的最大值从50调整为任意值。如果你不关心移动设备或遗留设备,只想支持现代桌面浏览器,那么支持Firefox的最低要求是150。有关浏览器限制的详细信息,请查看此页面。
在我们将productID拆分为它们自己的单独数组之后,我们为每个区块生成cookie。在本例中,我将其标记为qp
,并在其后面添加一个索引到块的[0]
标识符。
最后,我们启动我们的事件流。您希望有一个事件作为即时响应发生,这样您的浏览器就知道要清除新创建的cookie。在某个地方设置一个独立的功能来清除这些cookie也是明智的,以防从未收到响应,或者在收到响应之前刷新浏览器。
我建议将以下代码添加到上述代码块的开头,以便清理可能遗留的任何先前数据。
for (const cookie of Object.keys(Cookies.get()).filter(x => x.startsWith("qp"))){
Cookies.remove(cookie);
}
服务器端JS(Express)
async (request, response) => {
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache'
});
const productIDs = Object.entries(request.cookies).filter(([name, value]) => name.startsWith("qp")).map(([index, value]) => JSON.parse(value)).flat();
const sendEvent = (event, data) => response.write(`event: ${event}ndata: ${JSON.stringify(data)}nn`);
sendEvent("connected", {});
}
这里很简单。这种相当长的一行是一种非常简单的方法,可以获得所有阵列并将它们压平为一个阵列。只需确保startsWith("qp")
部分设置为您的cookie名称,并且显然确保您的网站上没有其他cookie以该字符串开头。
sendEvent()
是一个非常简单的功能,用于响应您的客户。只需确保立即响应connected
事件,以确保Cookie已正确清理。
就这样!这确实是一种非常简单的方法,虽然它在cookie限制方面有缺陷,但我觉得一个事先得到警告的开发人员将能够判断是否使用该技术。一旦成为一个易于重复使用的函数,它就很容易在任何需要的地方实现。