5

I have been struggling to write code that will make a POST request reliably on close of the tab window. Navigator.sendBeacon seems to be exactly what I need (I only require this to work for Google Chrome).

$(global).bind('unload', function () {
  let body = {
    UserEmail: appState.user.email,
    Job: {
      Id: appState.jobId
    },
    Timestamp: '/Date(' + new Date().getTime() + ')/',
    EventOrigin: 'PdfReviewClient',
    Event: 'JobClosed'
  };
  let headers = {
    Authorization: `JWT ${authenticationState.token}`,
    'Content-Type': 'application/json; charset=utf8'
  };
  let blob = new Blob([JSON.stringify(body)], headers);
  navigator.sendBeacon(configuration.rootApiUrl + 'jobevents', blob);
});

My beacon includes custom headers, that's why I create a Blob.

However, this request does not seem to be happening. This is especially hard to debug since the window closes. So the question is, why is my beacon not sending?

Scotty H
  • 6,432
  • 6
  • 41
  • 94
  • I'm assuming you verified that the request happens when *not* done on browser close? – Jorg Jan 18 '17 at 22:48
  • @Jorg Yes, I have. Good thought, thanks for checking, but that's not the problem in this case. – Scotty H Jan 18 '17 at 22:49
  • And the unload itself triggers, too? You might be able to test it with an `alert` to see if it blocks the window from closing – Jorg Jan 18 '17 at 22:52
  • @Jorg I wasn't able to test with an `alert` (may not be allowed, as it's [not allowed in onbeforeunload](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload) (see Notes section)), but I can debug through the code. – Scotty H Jan 18 '17 at 23:12

2 Answers2

5

The issue was the only header you can set with navigator.sendBeacon is Content-Type, and you set that by setting type in the Blob options. The server route had to be modified to accommodate the request without an Authorization header (I passed it as a URL parameter instead - weird for a POST request, but seemingly the only way to do that with a beacon). Here's how it looked in the end:

$(global).bind('unload', function () {
  if(appState.jobId == null) return;

  let headers = {
    type: 'application/json'
  };

  let jobEventLoggingBody = {
    UserEmail: appState.user.email,
    Job: {
      Id: appState.jobId
    },
    Timestamp: '/Date(' + new Date().getTime() + ')/',
    EventOrigin: 'PdfReviewClient',
    Event: 'JobClosed'
  };
  let jobEventLoggingUrl = `${configuration.rootApiUrl}jobevents?jwt=${authenticationState.token}`;
  let jobEventLoggingBlob = new Blob([JSON.stringify(jobEventLoggingBody)], headers);
  navigator.sendBeacon(jobEventLoggingUrl, jobEventLoggingBlob);
});

See also this question which specifically addresses sending headers in beacons.

Community
  • 1
  • 1
Scotty H
  • 6,432
  • 6
  • 41
  • 94
  • I ran into the same issue - I just had to send the data as a query string. Thanks for the q + a. Anyone know why this is (since usually query string is for GET, and since sendBeacon is a POST request, I expected the values I send to be in the body). – lmiller1990 Aug 04 '17 at 07:24
0

I don't know if it could help you. You could use this to debug.

function logData() {
  var newWindow = window.open();
  newWindow.document.write("ohai");
}

window.addEventListener("unload", logData, false); 
fumihwh
  • 1,239
  • 7
  • 13