Ember 2015 年重新处理 401

Ember Handling 401s Revisited in 2015

我可以在 Ember/Ember 数据中找到大量老问题 asking/answering 如何处理来自 Rails 后端的 401。许多,如果不是全部,在这一点上似乎已经过时了。我已经尝试了我能找到的一切。 (Ember-Data handling 401’s)

但无论我做什么,我总是在控制台中收到 401 错误,而 401 错误从未出现在我的代码中。如果在任何 place/model/anything 中遇到 401,我想要做的就是将重定向添加到“/”。我不需要任何身份验证检查或其他任何检查。

我已经尝试将其添加到应用程序路由操作以及路由器本身。

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

我也试过这个的几种变体。

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

我显然是个菜鸟,所以也许我在这里遗漏了一些简单的东西。我的 console.log 中的 None 被触发了,除非它是我引入的新错误,试图让它工作。是否有当前的 'best practice' 风格的方式来做到这一点?

控制器和路由

对于在控制器或路由中执行的逻辑,您通常会在路由上附加一个 error 操作。如果在控制器上触发了一个动作并且控制器没有处理那个动作(例如控制器 actions 哈希中没有 error 动作)那么它会自动传递给路由以获得机会处理 - 如果当前路由不处理操作,它会冒泡到父路由,直到它到达 ApplicationRoute.

我处理它的方式是有一个 AuthenticatedRoute 任何需要处理 401 的路由都从它延伸。此外,在您链接到的示例中 - 它具有 events 而现在是 actions.

像这样的东西应该适合你:

    App.AuthenticatedRoute = Ember.Route.extend({

        actions: {
        error: function(error) {
            if (!error || error.status !== 401) {
                // returning true bubbles the error to parent routes
                return true; 
            }

                // put your logic here
                console.log('this is a 401 error.');
            }
        }

    });

    App.IndexRoute = App.AuthenticatedRoute.extend({
        //logic
    });

    App.FooRoute = App.AuthenticatedRoute.extend({
        //logic
    });

    App.BarRoute = App.AuthenticatedRoute.extend({
        //logic
    });

组件

Ember 正在越来越多地转向组件 - 在组件中执行逻辑的事情是它与其他一切完全隔离。通常,您在组件中唯一可用的是您传递给它们的东西。

这意味着如果您在上述逻辑的组件 none 中执行某些逻辑 - 路由中存在的用于处理错误的逻辑 - 将被使用,除非您自己明确使用它。

如果您在组件中进行 store.find('person', 1)person.save() 之类的调用,您需要使用 .catch() 函数显式处理错误或将第二个函数传递给 .then().例如,以下两个语句会做同样的事情:

    store.find('person', 1).then(function(person) {
        console.log('found person 1:', person);
    }, function(err) {
        console.log('error finding person 1:', err);
    });

    store.find('person', 1).then(function(person) {
        console.log('found person 1:', person);
    }).catch(function(err) {
        console.log('error finding person 1:', err);
    });

这两个语句也是如此:

    person.save().then(function() {
        console.log('successfully saved person');
    }, function(err) {
        console.log('error saving person:', err);
    });

    person.save().then(function() {
        console.log('successfully saved person');
    }).catch(function(err) {
        console.log('error saving person:', err);
    });

在组件中重用路由错误逻辑

如果您想将组件中的错误处理传递给路由以进行处理,最好的方法是让组件触发一个操作并让调用 template/view/controller 处理它。

app/components/my-component.js

    Ember.Component.extend({
        'on-error': null,
        model: null,
        actions: {
            save: function() {
                var component = this;
                var model = this.get('model');

                mode.save().then(function() {
                    console.log('saved');
                }).catch(function(err) {
                    component.sendAction('on-error', err);
                });
            }
        }
    });

app/templates/components/my-component.hbs

    <button {{action 'save'}}>Save</button>

app/templates/index.hbs

    <!-- 
        Passing the 'error' action to the components 'on-error' property links the 'on-error' 
        action on the component with the 'error' action on the controller - if the controller 
        doesn't handle it, it's bubbled up to the route to handle
    -->

    {{my-component model=model on-error='error'}}

使用 Ember 1.13.8 & Ember 数据 1.13.9,我的认证路由使用:

actions: {
  error: function(reason, transition) {
    if (reason.errors[0].status === '401') {
      this.transitionTo('sign-in');
    }
  }
}

这里的其他两个答案都很好,可以正常工作,但恕我直言,有一些不受欢迎的警告。

我强烈建议实施 Ember.onerror 挂钩。任何未处理的 promise 拒绝都将在那个钩子上结束。如果您使用 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
      }
    };
  }
}

如上所述,这将处理所有未捕获的承诺拒绝。因此,这仍然为您提供了很大的灵活性,可以在组件中本地处理 promises。例如:

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

请注意,在上面的承诺拒绝处理程序中,如果我们无法处理错误,我们将重新抛出它。这个非常重要。重新抛出错误将为任何其他链式承诺处理程序提供处理它的机会。如果不加以处理,最终它将达到 Ember.onerror