Search code examples
javascriptwindowonbeforeunloadnavigatoronunload

Call a POST request for logging from window unload


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?


Solution

  • 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.