当前位置: 首页 > 知识库问答 >
问题:

服务器使用AJAX发送事件:如何使用XHR POST解决SSE GET?

呼延卓
2023-03-14

我试图解决AJAX和服务器发送事件之间的问题。我有一个应用程序,它向控制器发布一些说明,我希望控制器将一些注释作为事件发送回来,让用户知道请求的操作已经执行(可能有错误或需要一段时间)。

其思想是,用户可以通过客户端发送一个包含不同指令的包,并且服务器将在每个操作完成时通过SSE进行报告。

我通过Fiddler看到的问题是,当执行帖子时,它返回的响应包含我想使用的事件源消息。但是,事件源代码似乎也调用了一个GET,它似乎想要该事件源消息。因为它没有得到那个,所以连接反复关闭。

我目前有一些控制器代码如下:

    [System.Web.Http.HttpPost]
    public void Stop(ProjectViewModel model)
    {
        ProjectManager manager = new ProjectManager();
        if (model.Servers != null && model.Servers.Count != 0)
        {
            string machine = model.Servers[0];
            foreach (string service in model.Services)
            {
                manager.StopService(service, machine);
                Message("stop", service);
            }
        }
    }

在我看来,Ajax/XHR和server都发送了如下设置的事件:

var form = document.getElementById("submitform");

    form.onsubmit = function (e) {
        // stop the regular form submission
        e.preventDefault();

        // collect the form data while iterating over the inputs
        var data = {};
        for (var i = 0, ii = 2; i < ii; ++i) {
            var input = form[i];
            if (input.name == "Servers") {
                data[input.name] = document.getElementById("ServerSelect").options[document.getElementById("ServerSelect").selectedIndex].text;
            }
            else if (input.name == "Services")
                data[input.name] = document.getElementById("ServiceSelect").options[document.getElementById("ServiceSelect").selectedIndex].text;
        }
        if (action) { data["action"] = action };

        // construct an HTTP request
        var xhr = new XMLHttpRequest();
        if (action == "stop") {
            xhr.open(form.method, '/tools/project/stop', true);
        }

        if (action == "start") {
            xhr.open(form.method, '/tools/project/start', true)
        }

        xhr.setRequestHeader('Content-Type', 'application/json; charset=urf-8');



        // send the collected data as JSON
        xhr.send(JSON.stringify(data));

        xhr.onloadend = function () {
            // done
        };
    };

    function events() {
        if (window.EventSource == undefined) {
            // If not supported  
            document.getElementById('eventlog').innerHTML = "Your browser doesn't support Server Sent Events.";
        } else {
            var source = new EventSource('../tools/project/Stop');
            source.addEventListener("message", function (message) { console.log(message.data) });

            source.onopen = function (event) {
                document.getElementById('eventlog').innerHTML += 'Connection Opened.<br>';
                console.log("Open");
            };

            source.onerror = function (event) {
                if (event.eventPhase == EventSource.CLOSED) {
                    document.getElementById('eventlog').innerHTML += 'Connection Closed.<br>';
                    console.log("Close");
                }
            };

            source.onmessage = function (event) {
                //document.getElementById('eventlog').innerHTML += event.data + '<br>';
                var newElement = document.createElement("li");
                newElement.textContent = "message: " + event.data;
                document.getElementById("eventlog").appendChild(newElement)
                console.log("Message");
            };
        }
    };

我对Web开发有些陌生,我不知道如何解决这个问题。有没有办法让事件源消息从该POST中读取?或者将其发送到GET而不是作为对POST的响应发送?总的来说,似乎最糟糕的问题是我似乎无法将事件源api请求的事件消息发送到GET。

编辑:自从发布这篇文章以来,我尝试在控制器中创建一个新方法,专门处理eventsource请求,但似乎事件响应仍然以某种方式出现在POST响应正文中。

        public void Message(string action, string service)
    {
        Response.ContentType = "text/event-stream";
        Response.CacheControl = "no-cache";
        //Response.Write($"event: message\n");
        if (action == "stop")
        {
            Response.Write($"data: <li> {service} has stopped </li>\n\n");
        }
        Response.Flush();
        Thread.Sleep(1000);
        Response.Close();
    }

共有1个答案

干京
2023-03-14

我最终解决了这个问题。我最初的想法是使用字典在我的每个方法中来回传递viewmodel

https://code.msdn.microsoft.com/How-to-create-and-access-447ada98

我的最终实现如下所示:

public void Stop(ProjectViewModel model)
    {
        ProjectManager manager = new ProjectManager();
        if (model.Servers != null && model.Servers.Count != 0)
        {
            string machine = model.Servers[0];
            foreach (string service in model.Services)
            {
                manager.StopService(service, machine);
                model.events.Add(service, "stopped");
                this.Session["Events"] = model.events;
            }
        }
        //return View(model);
    }

public void Message(ProjectViewModel model)
    {
        Thread.Sleep(1000);
        Response.ContentType = "text/event-stream";
        Response.CacheControl = "no-cache";
        Response.AddHeader("connection", "keep-alive");
        var events = this.Session["Events"] as Dictionary<string, string>;
        Response.Write($"event: message\n");
        if (events != null && events.Count != 0)
        {
            foreach (KeyValuePair<string, string> message in events)
            {
                Response.Write($"data: {message.Key} has been {message.Value}\n\n");
            }
        }
        Response.Flush();
        Thread.Sleep(1000);
        Response.Close();

    }

在HTTP响应头中添加keep-alive-as-connection属性对于获取要发送的SSE和线程也很重要。由于停止操作和消息操作同时发生,因此使用了Sleep(1000)'s。我相信这方面会有一些优化,但就目前而言,这是功能性的,可以进一步开发。

 类似资料:
  • 问题内容: 我使用express.js设置了REST服务器。现在,我想向该服务器添加sse。实现此 sse程序包后,出现错误。我知道我收到此错误,何时尝试使用两次,但我没有。 我有可能无法在sse函数中使用express方法吗?例如: 此外,我找到了解决方案和这个。是否可以在不使用其他软件包的情况下通过该函数或其他方式使用sse ? 问题答案: 如果没有其他软件包,您绝对可以实现。 我写了一篇有关

  • 我正在尝试在Ruby Grape API上创建服务器发送的事件。问题是,连接似乎总是很快关闭,因为我一直在测试网页上看到连接关闭事件。 客户端连接到服务器,因为我可以看到正在调用的方法,但我想知道为什么连接不是恒定的,为什么我没有收到我使用Thread发送的数据。 以下是我的Ruby代码: 以下是我的Javascript: 编辑:我使用GET方法实现了它(我将Grape::API更改为Sinatr

  • 并在config/bootstrap.php中添加了以下行: 在config/events.php中 在lib/event/mylistener.php中 PS:我使用Cakephp事件是因为它允许我在一个地方从不同的控制器收集所需的数据,然后从那里,我可以创建事件流(服务器发送的事件)。如果有更好的选择,请分享。

  • 我使用nghttp2实现了一个REST服务器,它应该使用HTTP/2和服务器发送的事件(由浏览器中的EventSource使用)。然而,基于这些例子,我不清楚如何实现SSE。像在< code>asio-sv.cc中那样使用res.push()似乎不是正确的方法。 做这件事的正确方法是什么?我更喜欢使用nghttp2的C API,但是C API也可以。

  • 问题内容: 我正在尝试使用SSE将JSON数据发送到浏览器,但似乎无法正确处理,而且我也不知道为什么。 服务器端看起来像这样: 如您所见,我已经注释掉了帖子内容,但最终我希望将testdata用作JSON本身,如下所示: 客户端看起来像这样: 我看到控制台日志,但 没有看到警报。 问题答案: 尝试发送适当的JSON(输出中未引用): 但最好:

  • 我试图发送电子邮件使用gmail smtp使用javax.mail.下面是我的代码 代码工作正常当我在本地服务器上运行它时,但当我尝试在Elastic beanstek上运行它时(我的服务器在AWS EBS上运行),身份验证失败异常即将到来注意:我已打开从Google A/c设置访问不太安全的应用程序,但我仍然收到此错误 javax.mail.身份验证失败异常:534-5.7.14请通过您的网络浏