I had an interesting problem; we wanted to log stuff that was happening on a client’s browser. Fair enough, we can use AJAX to do that. However, this had a twist – we wanted to log events that were happening immediately before the page might be redirected to a new location. In other words, the page might unload, and a new page load. What I found was the messages from just before the page unloaded might not be sent to the server; as the page was unloading, the AJAX requests were not being performed. I wondered what I could do about this.
I did consider some kind of logic for “waiting until all AJAX calls have finished”, but that started to look quite complicated for what I was trying to do. Instead, I started to look into other options – and I found one.
Navigator.SendBeacon guarantees that the data is sent, even if the page is unloaded, and that it won’t affect the loading of the next page. Perfect. Available in most browsers.
if (navigator.sendBeacon) {
var postData = {
code: "...",
level: level,
message: message
};
var data = JSON.stringify(postData);
// Uses Navigator.SendBeacon as this is guaranteed to be send before
// page unload is complete and it doesn't handle a response.
// Can't send JSON as it's a security hazard and blocked by
// browsers, but we can stringify it and deserialize on the server end.
var blob = new Blob([data], { type: 'text/plain; charset=UTF-8' });
navigator.sendBeacon("/api/sage/stringlog", blob);
}
One quirk is that you can’t send JSON – apparently you used to be able to, but that was removed as it was a security hazard. But you can send your JSON as a plain text payload, and then just deserialize it at the other end:
public ActionResult StringLog(string value)
{
using (var reader = new StreamReader(this.Request.InputStream))
{
var data = reader.ReadToEnd();
var logMessage = JsonConvert.DeserializeObject<StringLogMessage>(data);
// do stuff.
}
}
So, it was a new problem for me, but there’s a good feature to support what I was trying to do – and it works just perfectly.