2013-05-10 75 views
6

我需要捕捉来自服务器的所有响应中可能的登录页面,所以我已全局覆盖Backbone.sync,以便在传递它们之前检查所有错误。捕捉骨干同步错误

Backbone.originalSync = Backbone.sync; 

Backbone.sync = function (method, model, options) { 
    var originalSuccess, originalError; 
    console.log("sync override..."); 
    // remember original success so we can call it if user logs in successfully 
    originalSuccess = options.success; 
    // proxy the error callback to first check if we get a login page back 
    originalError = options.error; 
    options.error = function (model, xhr, options) { 
     if (xhr.status === 200 && xhr.responseText === "") { 
      // parse error from empty response (jq1.9 invalid json, ok) 
      originalSuccess(model, xhr, options); 
     } else { 
      console.log("Sync error " + statusTxt + ", " + thrown.message); 
      if (xhr.status === 200 || xhr.status === 302 || xhr.status === 0) { 
       // login page returned instead of json... 
       // open a new window with relogon.html to trigger a new login 
       window.showModalDialog("../relogon.html"); 
      } else { 
       // normal error, pass along 
       if (originalError) { 
        originalError(model, xhr, options); 
       } 
      } 
     } 
    }; 

    // call the original sync 
    Backbone.originalSync(method, model, options); 
}; 

当从0.9.9变为1.0时,这个数字变得非常糟糕。看起来像原来的Backbone.sync以不同的方式包装它的错误处理程序,导致我的错误处理程序首先被调用,并带有jquery xhr签名。 我只好错误处理程序的签名改成这样:

options.error = function (xhr, statusTxt, thrown) { 

确定,所以现在它的工作原理,但我得到,我做错了什么的感觉。

有没有更好的方法来做到这一点?

我试过jQuery的承诺,但我需要能够从错误状态切换到成功(当调用originalSuccess时),这似乎没有与承诺一起工作。

+0

你可以显示你的jQuery尝试?这听起来很有希望[原文如此]。 – 2013-05-10 10:50:47

回答

8

你可以建立自己的jQuery Deferred object改变默认Backbone.sync行为

Backbone.sync = function (method, model, opts) { 
    var xhr, dfd; 

    dfd = $.Deferred(); 

    // opts.success and opts.error are resolved against the deferred object 
    // instead of the jqXHR object 
    if (opts) 
     dfd.then(opts.success, opts.error); 

    xhr = Backbone.originalSync(method, model, _.omit(opts, 'success', 'error')); 

    // success : forward to the deferred 
    xhr.done(dfd.resolve); 

    // failure : resolve or reject the deferred according to your cases 
    xhr.fail(function() { 
     if (xhr.status === 200 && xhr.responseText === "") { 
      dfd.resolve.apply(xhr, arguments); 
     } else { 
      if (xhr.status === 200 || xhr.status === 302 || xhr.status === 0) { 
       console.log('login'); 
      } 
      dfd.reject.apply(xhr, arguments); 
     } 
    }); 

    // return the promise to add callbacks if necessary 
    return dfd.promise(); 
}; 

的承诺反映您所选择的最终状态。

http://jsfiddle.net/AsMYQ/4/对于失败演示,http://jsfiddle.net/AsMYQ/5/取得成功。

如果我可以

  • 你可能不应该与你的登录操作绑得那么紧Backbone.sync。使用事件,从骨干或jQuery.ajaxError作为@Andrey建议
  • 您的服务器响应应指示授权失败,可能是一个401个状态
  • 当你重写同步别忘了来回报您的延期承诺/ jqXHR对象,可能出现在方便的下线
+0

谢谢,现在我看到我在尝试延迟处理时做了什么错误。关于你的建议,你可能是对的 - 我会试图解耦relogin,更糟的是我没有任何控制服务器响应(有一个传统的SSO拦截所有调用) – d4kris 2013-05-13 20:28:03

+0

为什么你不返回xhr对象你的新同步()函数,而不是做出新的承诺? – 2014-11-10 13:25:57

+0

因为成功和失败的条件不是由xhr定义的条件 – nikoshr 2014-11-10 13:29:09

9

所有同步错误都会传递给模型的error事件,因此您可以收听此事件。

http://backbonejs.org/#Events-catalog

“错误”(型号,XHR,选项) - 当一个模型保存在服务器上调用失败。

要捕获错误全球您可以使用http://api.jquery.com/ajaxError/

+0

是否有可能在全球范围内收听所有模型和集合?这个错误可能随时发生,所以我想在全球范围内处理它。无论如何,我需要捕捉错误并对其进行处理,而不是只听它,所以我不认为这会适用于我的情况。 – d4kris 2013-05-10 10:44:46

+0

我已经用可能的解决方案更新了我的答案。 – 2013-05-10 11:24:11

+0

难道你不是在用jquery ajax处理程序在这里混淆骨干事件吗? AFAIK,ajaxError与主干错误事件根本没有关系;例如,如果您手动触发错误事件,则不会触发ajaxError处理程序,并且回调的参数会有所不同 – SpoonMeiser 2016-03-29 10:19:50