Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember Handling 401s Revisited in 2015

I can find a ton of old questions asking/answering how to handle 401s from the Rails backend in Ember/Ember Data. Many, if not all, seem to be outdated at this point. I have tried everything I could find. (Ember-Data handling 401’s)

But no matter what I do I just keep getting 401 errors in the console while the 401s are never caught in my code. All I want to do is add a redirect to '/' if a 401 is ever encountered in any place/model/anything. I don't need any checking for authentication or anything else.

I have tried adding this to the Application Route actions as well as to to the Router itself.

error: function (error, transition) {
  console.log("err: " + error.status);
  if (error && error.status === 401) {
    return this.transitionToRoute('/');
  }
}

I've also tried several variations of this https://stackoverflow.com/a/19063009/1850353.

App.ApplicationAdapter = DS.RESTAdapter.extend({
  ajaxError: function(jqXHR) {
    var error = this._super(jqXHR);
    console.log("jqXHR: " + jqXHR.status);
    if (jqXHR && jqXHR.status === 401) {
      #handle the 401 error
    }
    return error;
  }
});

I'm obviously a noob so maybe I'm missing something simple here. None of my console.log's are getting triggered unless it's for a new error I've introduced trying to get this to work. Is there a current, 'best practice' style way of doing this?

like image 239
Confused Avatar asked Apr 28 '15 17:04

Confused


1 Answers

Both of the two other answers here are good and will work fine, but have a few undesirable caveats IMHO.

I would strongly recommend implementing an Ember.onerror hook. Any unhandled promise rejections will end up at that hook. You can implement it with an initializer if you're using Ember-CLI.

// app/initializers/on-error.js
export default {
  name: 'on-error',
  initialize: function(container, app) {
    Ember.onerror = function(error) {
      if(error && error.status === 401) {
        // Ember 2.1 adds a public lookup function. Older versions
        // need to use the container.
        var router = app.lookup ? app.lookup('router:main') :
                     app.__container__.lookup('router:main');
        // Even future versions of ember are likely to have an 
        // accessible router service to do this work.
        router.transitionTo('sign-in');
      }

      if(error && error.stack) {
        // Uncaught exception - Log stacktrace to server
      }
    };
  }
}

As mentioned above, this will handle all uncaught promise rejections. So this still provides you with a good bit of flexibility to handle promises locally in a component. For example:

export default Ember.Component.extend({
  actions: {
    saveChanges: function() {
      this.get('model').save().then(null, (reason) => {
        if(/* reason is validation error */) {
          this.set('validationError', reason.errorMsg);
        } else {
          throw reason;
        }
      })
    }
  }
 });

Take notice that in the promise rejection handler above if we couldn't handle the error, we rethrow it. This is very important. Rethrowing the error will give any additional chained promise handlers the chance to deal with it. And ultimately it will make it's way up to Ember.onerror if left unhandled.

like image 78
Wesley Workman Avatar answered Nov 13 '22 18:11

Wesley Workman