2013-05-10 19 views
6

Avevo bisogno di catturare una possibile pagina di accesso in tutte le risposte dal server, quindi ho sovrascritto Backbone.sync a livello globale in modo da poter controllare tutti gli errori prima di passarli.Errori di sincronizzazione Backbone

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

Questo si è rotto miseramente passando da 0.9.9 a 1.0. Sembra che l'originale Backbone.sync avvolga in modo diverso i suoi gestori di errori, facendo in modo che il mio gestore di errori venga chiamato per primo, con una firma jquery xhr. Ho dovuto cambiare la firma del gestore degli errori a questo:

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

Ok, quindi ora funziona, ma ho la sensazione che sto facendo qualcosa di sbagliato.

C'è un modo migliore per farlo?

Ho provato con jquery promises, ma ho bisogno di essere in grado di passare dallo stato di errore al successo (quando si chiama originalSuccess), che non sembra funzionare con le promesse.

+0

Puoi mostrare il tuo tentativo di jQuery? Sembra promettente [sic]. –

risposta

8

È possibile costruire il proprio jQuery Deferred object per modificare il valore predefinito Backbone.sync comportamento

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

La promessa riflette lo stato finale che si sceglie.

http://jsfiddle.net/AsMYQ/4/ per una demo di errore, http://jsfiddle.net/AsMYQ/5/ per un successo.

E se posso

  • probabilmente non dovrebbe legare così strettamente Backbone.sync con la movimentazione di accesso. Utilizzare gli eventi, da Backbone o jQuery.ajaxError come @Andrey suggerito
  • la risposta del server dovrebbe indicare un errore di autorizzazione, probabilmente uno status 401
  • non dimenticate di restituire la tua promessa differita/oggetto jqXHR quando si modifica la sincronizzazione, che potrebbe venire utile lungo la linea
+0

Grazie, ora vedo cosa ho sbagliato quando ho provato con il differito.Per quanto riguarda i tuoi suggerimenti, hai probabilmente ragione - cercherò di disaccoppiare il relogin, cosa peggiore è che non ho alcun controllo sulle risposte del server (c'è un SSO legacy che intercetta tutte le chiamate) – d4kris

+0

Perché non restituisci l'oggetto xhr da la tua nuova funzione sync(), invece di fare una nuova promessa? –

+0

Poiché le condizioni per il successo e l'insuccesso non sono quelle definite dalla xhr – nikoshr

9

Tutti gli errori di sincronizzazione vengono passati all'evento error del modello, quindi è possibile ascoltare questo evento.

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

"errore" (modello, XHR, opzioni) - quando un modello di salvataggio chiamata non riesce sul server.

Per cattura errori a livello globale è possibile utilizzare http://api.jquery.com/ajaxError/

+0

È possibile ascoltare tutti i modelli e le raccolte a livello globale? Questo errore può verificarsi in qualsiasi momento, quindi mi piacerebbe gestirlo a livello globale. Ad ogni modo, ho bisogno di catturare l'errore e gestirlo, non solo ascoltarlo, quindi non penso che ciò funzionerebbe nel mio scenario. – d4kris

+0

Ho aggiornato la mia risposta con la possibile soluzione. –

+0

Non stai confondendo gli eventi di backbone con jquery ajax handler qui? AFAIK, ajaxError non è affatto correlato all'evento di errore di backbone; se si attiva manualmente un evento di errore, ad esempio, non si attiva il gestore ajaxError e gli argomenti della richiamata saranno diversi – SpoonMeiser