Backbone.js同步错误处理 #

一、错误类型 #

1.1 常见错误类型 #

text
同步错误类型
├── HTTP错误:401、403、404、500等
├── 验证错误:服务器端验证失败
├── 网络错误:连接超时、断网
└── 解析错误:JSON解析失败

1.2 错误响应格式 #

json
{
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "验证失败",
        "details": {
            "name": ["不能为空"],
            "email": ["格式不正确"]
        }
    }
}

二、错误回调 #

2.1 基本错误处理 #

javascript
user.fetch({
    success: function(model, response) {
        console.log('成功:', response);
    },
    error: function(model, xhr, options) {
        console.log('失败:', xhr.status, xhr.statusText);
    }
});

2.2 error事件 #

javascript
user.on('error', function(model, xhr, options) {
    console.log('模型错误:', xhr.status);
});

user.fetch();

2.3 invalid事件 #

javascript
user.on('invalid', function(model, error, options) {
    console.log('验证失败:', error);
});

user.save({ name: '' }, { validate: true });

三、HTTP错误处理 #

3.1 状态码处理 #

javascript
user.fetch({
    error: function(model, xhr) {
        switch (xhr.status) {
            case 400:
                console.log('请求参数错误');
                break;
            case 401:
                console.log('未授权,请登录');
                break;
            case 403:
                console.log('禁止访问');
                break;
            case 404:
                console.log('资源不存在');
                break;
            case 422:
                console.log('验证错误');
                break;
            case 500:
                console.log('服务器错误');
                break;
            default:
                console.log('未知错误');
        }
    }
});

3.2 全局错误处理 #

javascript
$(document).ajaxError(function(event, xhr, settings, error) {
    switch (xhr.status) {
        case 401:
            Backbone.history.navigate('login', { trigger: true });
            break;
        case 403:
            alert('您没有权限执行此操作');
            break;
        case 500:
            alert('服务器错误,请稍后重试');
            break;
    }
});

3.3 认证错误处理 #

javascript
var AuthHandler = {
    handle401: function() {
        localStorage.removeItem('token');
        Backbone.history.navigate('login', { trigger: true });
    },
    
    setup: function() {
        $(document).ajaxError(function(event, xhr) {
            if (xhr.status === 401) {
                this.handle401();
            }
        }.bind(this));
    }
};

AuthHandler.setup();

四、验证错误处理 #

4.1 服务器验证错误 #

javascript
user.save(attrs, {
    error: function(model, xhr) {
        if (xhr.status === 422) {
            var errors = xhr.responseJSON.errors;
            model.trigger('validation:error', errors);
        }
    }
});

4.2 显示验证错误 #

javascript
var UserFormView = Backbone.View.extend({
    events: {
        'submit': 'submit'
    },
    
    initialize: function() {
        this.listenTo(this.model, 'validation:error', this.showErrors);
        this.listenTo(this.model, 'invalid', this.showInvalidError);
    },
    
    submit: function(e) {
        e.preventDefault();
        
        var data = {
            name: this.$('#name').val(),
            email: this.$('#email').val()
        };
        
        this.model.save(data, {
            error: function(model, xhr) {
                if (xhr.status === 422) {
                    model.trigger('validation:error', xhr.responseJSON.errors);
                }
            }
        });
    },
    
    showErrors: function(errors) {
        this.clearErrors();
        
        for (var field in errors) {
            var $field = this.$('[name="' + field + '"]');
            $field.addClass('error');
            $field.after('<span class="error-message">' + errors[field].join(', ') + '</span>');
        }
    },
    
    showInvalidError: function(model, error) {
        this.$('.alert').text(error).show();
    },
    
    clearErrors: function() {
        this.$('.error').removeClass('error');
        this.$('.error-message').remove();
    }
});

五、网络错误处理 #

5.1 超时处理 #

javascript
user.fetch({
    timeout: 10000,
    error: function(model, xhr) {
        if (xhr.statusText === 'timeout') {
            console.log('请求超时');
        }
    }
});

5.2 重试机制 #

javascript
var RetryMixin = {
    maxRetries: 3,
    retryDelay: 1000,
    
    fetchWithRetry: function(options) {
        var self = this;
        var retries = 0;
        
        options = options || {};
        
        function attempt() {
            return self.fetch(options).fail(function(xhr) {
                if (xhr.status >= 500 && retries < self.maxRetries) {
                    retries++;
                    console.log('重试第', retries, '次');
                    return $.Deferred(function(deferred) {
                        setTimeout(function() {
                            attempt().done(deferred.resolve).fail(deferred.reject);
                        }, self.retryDelay * retries);
                    });
                }
            });
        }
        
        return attempt();
    }
};

5.3 离线处理 #

javascript
var OfflineHandler = {
    isOnline: function() {
        return navigator.onLine;
    },
    
    setup: function() {
        $(window).on('offline', function() {
            console.log('网络已断开');
            this.showOfflineMessage();
        }.bind(this));
        
        $(window).on('online', function() {
            console.log('网络已恢复');
            this.hideOfflineMessage();
        }.bind(this));
    },
    
    showOfflineMessage: function() {
        $('body').append('<div id="offline-message" class="alert">您已离线</div>');
    },
    
    hideOfflineMessage: function() {
        $('#offline-message').remove();
    }
};

OfflineHandler.setup();

六、错误显示 #

6.1 通知服务 #

javascript
var NotificationService = _.extend({
    show: function(type, message) {
        var $notification = $(
            '<div class="notification notification-' + type + '">' +
            '  <span>' + message + '</span>' +
            '  <button class="close">×</button>' +
            '</div>'
        );
        
        $notification.find('.close').on('click', function() {
            $notification.remove();
        });
        
        $('#notifications').append($notification);
        
        setTimeout(function() {
            $notification.fadeOut(function() {
                $(this).remove();
            });
        }, 5000);
    },
    
    success: function(message) {
        this.show('success', message);
    },
    
    error: function(message) {
        this.show('error', message);
    },
    
    warning: function(message) {
        this.show('warning', message);
    },
    
    info: function(message) {
        this.show('info', message);
    }
}, Backbone.Events);

6.2 全局错误处理 #

javascript
var GlobalErrorHandler = {
    setup: function() {
        $(document).ajaxError(function(event, xhr, settings, error) {
            var message;
            
            switch (xhr.status) {
                case 0:
                    message = '网络连接失败,请检查网络';
                    break;
                case 400:
                    message = '请求参数错误';
                    break;
                case 401:
                    message = '登录已过期,请重新登录';
                    Backbone.history.navigate('login', { trigger: true });
                    break;
                case 403:
                    message = '您没有权限执行此操作';
                    break;
                case 404:
                    message = '请求的资源不存在';
                    break;
                case 422:
                    var errors = xhr.responseJSON && xhr.responseJSON.errors;
                    if (errors) {
                        message = Object.values(errors).join(', ');
                    } else {
                        message = '数据验证失败';
                    }
                    break;
                case 500:
                    message = '服务器错误,请稍后重试';
                    break;
                default:
                    message = '未知错误: ' + xhr.status;
            }
            
            NotificationService.error(message);
        });
    }
};

GlobalErrorHandler.setup();

七、实用示例 #

7.1 错误处理模型 #

javascript
var ErrorHandlingModel = Backbone.Model.extend({
    fetch: function(options) {
        var self = this;
        
        this.trigger('request:start');
        
        options = options || {};
        var success = options.success;
        var error = options.error;
        
        options.success = function() {
            self.trigger('request:end');
            self.trigger('request:success');
            if (success) success.apply(this, arguments);
        };
        
        options.error = function(xhr) {
            self.trigger('request:end');
            self.trigger('request:error', xhr);
            
            self.handleError(xhr);
            
            if (error) error.apply(this, arguments);
        };
        
        return Backbone.Model.prototype.fetch.call(this, options);
    },
    
    handleError: function(xhr) {
        var error = {
            status: xhr.status,
            message: this.getErrorMessage(xhr),
            details: xhr.responseJSON
        };
        
        this.lastError = error;
        this.trigger('error', this, error);
    },
    
    getErrorMessage: function(xhr) {
        if (xhr.responseJSON && xhr.responseJSON.message) {
            return xhr.responseJSON.message;
        }
        
        switch (xhr.status) {
            case 401: return '未授权';
            case 403: return '禁止访问';
            case 404: return '资源不存在';
            case 500: return '服务器错误';
            default: return '请求失败';
        }
    }
});

7.2 错误处理视图 #

javascript
var ErrorHandlingView = Backbone.View.extend({
    initialize: function() {
        this.listenTo(this.model, 'request:start', this.showLoading);
        this.listenTo(this.model, 'request:end', this.hideLoading);
        this.listenTo(this.model, 'request:error', this.showError);
    },
    
    showLoading: function() {
        this.$('.loading').show();
    },
    
    hideLoading: function() {
        this.$('.loading').hide();
    },
    
    showError: function(xhr) {
        var message = this.model.getErrorMessage(xhr);
        this.$('.error-message').text(message).show();
    }
});

八、总结 #

8.1 错误处理方法 #

方法 说明
error回调 单次请求错误处理
error事件 模型级别错误处理
ajaxError 全局错误处理

8.2 最佳实践 #

  1. 实现全局错误处理
  2. 统一错误显示方式
  3. 处理认证错误
  4. 实现重试机制
  5. 处理离线状态
最后更新:2026-03-28