Server Sent Events with AJAX: How to resolve SSE GET with XHR POST?












1















I'm trying to resolve an issue between, what I perceive is, AJAX and Server Sent Events. I have an application that does a post with some instructions to the controller, and I would like the controller to send some commentary back as an event to let the user know that the action requested has been performed (can have errors or take a while).



The idea is that the user can send a package of different instructions through the client, and the server will report through SSE when each of these actions are completed.



The problem I see through Fiddler is that when the post is performed, the response that it gets back contains my eventsource message that I would like used. However, the eventsource code also appears to call a GET, in which it appears to want that eventsource message. Because it doesn't get that, the connection repeatedly closes.



I currently have some controller code like so:



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


and in my view, both Ajax/XHR and server sent events set up like so:



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


I'm somewhat new to web development, and I'm not sure how to resolve this issue. Is there a way I can have the eventsource message read from that POST? Or have it sent to the GET instead of being sent as a response to the POST? Overall, it seems that the most damning issue is that I can't seem to get the event messages sent to the GET that is requested by the eventsource api.



EDIT: Since posting this, I tried creating a new method in the controller that specifically handles eventsource requests, but it appears that the event response still somehow ends up in the POST response body.



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









share|improve this question




















  • 1





    Why Response.Close(); inside foreach block, since it ends response?

    – Abdelrahman M. Allam
    Jan 2 at 21:01













  • @AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

    – nostalgk
    Jan 2 at 21:02











  • Ok, lets moving using Response.Close(); within if(action == 'stop') block only

    – Abdelrahman M. Allam
    Jan 2 at 21:19













  • Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

    – nostalgk
    Jan 2 at 21:20
















1















I'm trying to resolve an issue between, what I perceive is, AJAX and Server Sent Events. I have an application that does a post with some instructions to the controller, and I would like the controller to send some commentary back as an event to let the user know that the action requested has been performed (can have errors or take a while).



The idea is that the user can send a package of different instructions through the client, and the server will report through SSE when each of these actions are completed.



The problem I see through Fiddler is that when the post is performed, the response that it gets back contains my eventsource message that I would like used. However, the eventsource code also appears to call a GET, in which it appears to want that eventsource message. Because it doesn't get that, the connection repeatedly closes.



I currently have some controller code like so:



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


and in my view, both Ajax/XHR and server sent events set up like so:



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


I'm somewhat new to web development, and I'm not sure how to resolve this issue. Is there a way I can have the eventsource message read from that POST? Or have it sent to the GET instead of being sent as a response to the POST? Overall, it seems that the most damning issue is that I can't seem to get the event messages sent to the GET that is requested by the eventsource api.



EDIT: Since posting this, I tried creating a new method in the controller that specifically handles eventsource requests, but it appears that the event response still somehow ends up in the POST response body.



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









share|improve this question




















  • 1





    Why Response.Close(); inside foreach block, since it ends response?

    – Abdelrahman M. Allam
    Jan 2 at 21:01













  • @AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

    – nostalgk
    Jan 2 at 21:02











  • Ok, lets moving using Response.Close(); within if(action == 'stop') block only

    – Abdelrahman M. Allam
    Jan 2 at 21:19













  • Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

    – nostalgk
    Jan 2 at 21:20














1












1








1








I'm trying to resolve an issue between, what I perceive is, AJAX and Server Sent Events. I have an application that does a post with some instructions to the controller, and I would like the controller to send some commentary back as an event to let the user know that the action requested has been performed (can have errors or take a while).



The idea is that the user can send a package of different instructions through the client, and the server will report through SSE when each of these actions are completed.



The problem I see through Fiddler is that when the post is performed, the response that it gets back contains my eventsource message that I would like used. However, the eventsource code also appears to call a GET, in which it appears to want that eventsource message. Because it doesn't get that, the connection repeatedly closes.



I currently have some controller code like so:



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


and in my view, both Ajax/XHR and server sent events set up like so:



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


I'm somewhat new to web development, and I'm not sure how to resolve this issue. Is there a way I can have the eventsource message read from that POST? Or have it sent to the GET instead of being sent as a response to the POST? Overall, it seems that the most damning issue is that I can't seem to get the event messages sent to the GET that is requested by the eventsource api.



EDIT: Since posting this, I tried creating a new method in the controller that specifically handles eventsource requests, but it appears that the event response still somehow ends up in the POST response body.



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









share|improve this question
















I'm trying to resolve an issue between, what I perceive is, AJAX and Server Sent Events. I have an application that does a post with some instructions to the controller, and I would like the controller to send some commentary back as an event to let the user know that the action requested has been performed (can have errors or take a while).



The idea is that the user can send a package of different instructions through the client, and the server will report through SSE when each of these actions are completed.



The problem I see through Fiddler is that when the post is performed, the response that it gets back contains my eventsource message that I would like used. However, the eventsource code also appears to call a GET, in which it appears to want that eventsource message. Because it doesn't get that, the connection repeatedly closes.



I currently have some controller code like so:



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


and in my view, both Ajax/XHR and server sent events set up like so:



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


I'm somewhat new to web development, and I'm not sure how to resolve this issue. Is there a way I can have the eventsource message read from that POST? Or have it sent to the GET instead of being sent as a response to the POST? Overall, it seems that the most damning issue is that I can't seem to get the event messages sent to the GET that is requested by the eventsource api.



EDIT: Since posting this, I tried creating a new method in the controller that specifically handles eventsource requests, but it appears that the event response still somehow ends up in the POST response body.



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






javascript ajax asp.net-mvc server-sent-events






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 15:14







nostalgk

















asked Jan 2 at 19:42









nostalgknostalgk

164115




164115








  • 1





    Why Response.Close(); inside foreach block, since it ends response?

    – Abdelrahman M. Allam
    Jan 2 at 21:01













  • @AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

    – nostalgk
    Jan 2 at 21:02











  • Ok, lets moving using Response.Close(); within if(action == 'stop') block only

    – Abdelrahman M. Allam
    Jan 2 at 21:19













  • Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

    – nostalgk
    Jan 2 at 21:20














  • 1





    Why Response.Close(); inside foreach block, since it ends response?

    – Abdelrahman M. Allam
    Jan 2 at 21:01













  • @AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

    – nostalgk
    Jan 2 at 21:02











  • Ok, lets moving using Response.Close(); within if(action == 'stop') block only

    – Abdelrahman M. Allam
    Jan 2 at 21:19













  • Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

    – nostalgk
    Jan 2 at 21:20








1




1





Why Response.Close(); inside foreach block, since it ends response?

– Abdelrahman M. Allam
Jan 2 at 21:01







Why Response.Close(); inside foreach block, since it ends response?

– Abdelrahman M. Allam
Jan 2 at 21:01















@AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

– nostalgk
Jan 2 at 21:02





@AbdelrahmanM.Allam Sorry, that has since been removed, but either way it didn't seem to be affecting behavior. I'll update the code.

– nostalgk
Jan 2 at 21:02













Ok, lets moving using Response.Close(); within if(action == 'stop') block only

– Abdelrahman M. Allam
Jan 2 at 21:19







Ok, lets moving using Response.Close(); within if(action == 'stop') block only

– Abdelrahman M. Allam
Jan 2 at 21:19















Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

– nostalgk
Jan 2 at 21:20





Yes, that is the case. No specific reason, just figured it'd be cleaner to close the response (especially during debug sessions) after the work has been done.

– nostalgk
Jan 2 at 21:20












1 Answer
1






active

oldest

votes


















0














I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:



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



My final implementation looks like this:



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: messagen");
if (events != null && events.Count != 0)
{
foreach (KeyValuePair<string, string> message in events)
{
Response.Write($"data: {message.Key} has been {message.Value}nn");
}
}
Response.Flush();
Thread.Sleep(1000);
Response.Close();

}


Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.






share|improve this answer
























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54012237%2fserver-sent-events-with-ajax-how-to-resolve-sse-get-with-xhr-post%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:



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



    My final implementation looks like this:



    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: messagen");
    if (events != null && events.Count != 0)
    {
    foreach (KeyValuePair<string, string> message in events)
    {
    Response.Write($"data: {message.Key} has been {message.Value}nn");
    }
    }
    Response.Flush();
    Thread.Sleep(1000);
    Response.Close();

    }


    Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.






    share|improve this answer




























      0














      I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:



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



      My final implementation looks like this:



      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: messagen");
      if (events != null && events.Count != 0)
      {
      foreach (KeyValuePair<string, string> message in events)
      {
      Response.Write($"data: {message.Key} has been {message.Value}nn");
      }
      }
      Response.Flush();
      Thread.Sleep(1000);
      Response.Close();

      }


      Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.






      share|improve this answer


























        0












        0








        0







        I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:



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



        My final implementation looks like this:



        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: messagen");
        if (events != null && events.Count != 0)
        {
        foreach (KeyValuePair<string, string> message in events)
        {
        Response.Write($"data: {message.Key} has been {message.Value}nn");
        }
        }
        Response.Flush();
        Thread.Sleep(1000);
        Response.Close();

        }


        Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.






        share|improve this answer













        I ended up solving this. My original idea was to pass the viewmodel in each of my methods back and forth with a Dictionary<string,string> to key in each event that can be used, but the viewmodel is not persistent. I solved this issue further by implementing the events in a Dictionary saved in Session data, and the usage of Sessions for MVC can be found in the resource here that I used:



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



        My final implementation looks like this:



        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: messagen");
        if (events != null && events.Count != 0)
        {
        foreach (KeyValuePair<string, string> message in events)
        {
        Response.Write($"data: {message.Key} has been {message.Value}nn");
        }
        }
        Response.Flush();
        Thread.Sleep(1000);
        Response.Close();

        }


        Adding keep-alive as connection attribute in the HTTP Response header was also important to getting the SSEs to send, and the Thread.Sleep(1000)'s are used due to the stop action and message action happening simultaneously. I'm sure there's some optimizations that can go into this, but for now, this is functional and able to be further developed.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jan 3 at 19:19









        nostalgknostalgk

        164115




        164115
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54012237%2fserver-sent-events-with-ajax-how-to-resolve-sse-get-with-xhr-post%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            How to fix TextFormField cause rebuild widget in Flutter

            Npm cannot find a required file even through it is in the searched directory