Backbone.js RESTful API #

一、RESTful概述 #

1.1 REST原则 #

text
RESTful API设计原则
├── 资源导向:URL表示资源
├── HTTP方法:使用标准HTTP方法
├── 无状态:每个请求独立
└── 统一接口:一致的API设计

1.2 HTTP方法映射 #

操作 HTTP方法 URL 说明
Create POST /users 创建资源
Read GET /users/:id 获取单个资源
Read GET /users 获取资源列表
Update PUT /users/:id 完整更新
Update PATCH /users/:id 部分更新
Delete DELETE /users/:id 删除资源

二、URL配置 #

2.1 urlRoot #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users'
});

var user = new User({ id: 1 });
console.log(user.url());

2.2 url方法 #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users',
    
    url: function() {
        if (this.isNew()) {
            return this.urlRoot;
        }
        return this.urlRoot + '/' + this.id;
    }
});

2.3 集合URL #

javascript
var Users = Backbone.Collection.extend({
    model: User,
    url: '/api/users'
});

var users = new Users();
console.log(users.url);

2.4 动态URL #

javascript
var User = Backbone.Model.extend({
    urlRoot: function() {
        return '/api/' + this.get('type') + '/users';
    }
});

var user = new User({ type: 'admin', id: 1 });
console.log(user.url());

2.5 嵌套资源URL #

javascript
var Comment = Backbone.Model.extend({
    initialize: function(attrs, options) {
        this.postId = options.postId;
    },
    
    urlRoot: function() {
        return '/api/posts/' + this.postId + '/comments';
    }
});

var comment = new Comment({ id: 1 }, { postId: 123 });
console.log(comment.url());

三、请求格式 #

3.1 默认请求格式 #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users'
});

var user = new User();
user.save({ name: '张三', email: 'zhangsan@example.com' });

请求体:

json
{
    "name": "张三",
    "email": "zhangsan@example.com"
}

3.2 自定义请求头 #

javascript
user.save(attrs, {
    headers: {
        'X-Custom-Header': 'value',
        'Accept': 'application/json'
    }
});

3.3 自定义Content-Type #

javascript
Backbone.ajax = function(request) {
    request.contentType = 'application/json';
    return $.ajax(request);
};

3.4 发送前处理 #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users',
    
    sync: function(method, model, options) {
        if (method === 'create' || method === 'update') {
            options.data = JSON.stringify({
                user: model.toJSON()
            });
        }
        return Backbone.sync(method, model, options);
    }
});

四、响应处理 #

4.1 parse方法 #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users',
    
    parse: function(response) {
        return response.data || response;
    }
});

4.2 处理嵌套数据 #

javascript
var User = Backbone.Model.extend({
    parse: function(response) {
        response.profile = new Profile(response.profile);
        return response;
    }
});

4.3 集合parse #

javascript
var Users = Backbone.Collection.extend({
    url: '/api/users',
    
    parse: function(response) {
        this.total = response.total;
        this.page = response.page;
        return response.items;
    }
});

4.4 处理分页 #

javascript
var Users = Backbone.Collection.extend({
    url: '/api/users',
    
    parse: function(response) {
        this.pagination = {
            total: response.total,
            page: response.page,
            perPage: response.per_page,
            totalPages: response.total_pages
        };
        return response.data;
    }
});

五、API示例 #

5.1 用户API #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users',
    
    defaults: {
        name: '',
        email: '',
        role: 'user'
    },
    
    validate: function(attrs) {
        if (!attrs.name) return '姓名不能为空';
        if (!attrs.email) return '邮箱不能为空';
    },
    
    parse: function(response) {
        return response.user || response;
    }
});

var Users = Backbone.Collection.extend({
    model: User,
    url: '/api/users',
    
    parse: function(response) {
        return response.users || response;
    },
    
    getActive: function() {
        return this.filter(function(user) {
            return user.get('active');
        });
    }
});

5.2 文章API #

javascript
var Post = Backbone.Model.extend({
    urlRoot: '/api/posts',
    
    defaults: {
        title: '',
        content: '',
        author: null,
        tags: [],
        published: false
    },
    
    publish: function() {
        this.save({ published: true }, {
            url: this.url() + '/publish'
        });
    },
    
    unpublish: function() {
        this.save({ published: false }, {
            url: this.url() + '/unpublish'
        });
    },
    
    addComment: function(content) {
        var self = this;
        return $.ajax({
            url: this.url() + '/comments',
            method: 'POST',
            data: JSON.stringify({ content: content }),
            contentType: 'application/json'
        });
    }
});

5.3 搜索API #

javascript
var SearchCollection = Backbone.Collection.extend({
    url: '/api/search',
    
    search: function(query, options) {
        options = options || {};
        options.data = { q: query };
        return this.fetch(options);
    },
    
    parse: function(response) {
        this.query = response.query;
        this.total = response.total;
        return response.results;
    }
});

var searchResults = new SearchCollection();
searchResults.search('backbone').then(function() {
    console.log('找到', searchResults.total, '条结果');
});

六、错误处理 #

6.1 全局错误处理 #

javascript
$(document).ajaxError(function(event, xhr, settings, error) {
    if (xhr.status === 401) {
        console.log('未授权,请登录');
    } else if (xhr.status === 403) {
        console.log('禁止访问');
    } else if (xhr.status === 404) {
        console.log('资源不存在');
    } else if (xhr.status >= 500) {
        console.log('服务器错误');
    }
});

6.2 模型错误处理 #

javascript
user.save(attrs, {
    success: function(model, response) {
        console.log('保存成功');
    },
    error: function(model, xhr) {
        if (xhr.status === 422) {
            var errors = xhr.responseJSON.errors;
            console.log('验证错误:', errors);
        }
    }
});

6.3 错误事件 #

javascript
user.on('error', function(model, xhr) {
    console.log('请求失败:', xhr.status);
});

user.fetch();

七、实用示例 #

7.1 API客户端 #

javascript
var APIClient = {
    baseUrl: '/api',
    
    request: function(method, path, data) {
        return $.ajax({
            url: this.baseUrl + path,
            method: method,
            data: data ? JSON.stringify(data) : null,
            contentType: 'application/json',
            headers: {
                'Authorization': 'Bearer ' + localStorage.getItem('token')
            }
        });
    },
    
    get: function(path) {
        return this.request('GET', path);
    },
    
    post: function(path, data) {
        return this.request('POST', path, data);
    },
    
    put: function(path, data) {
        return this.request('PUT', path, data);
    },
    
    patch: function(path, data) {
        return this.request('PATCH', path, data);
    },
    
    delete: function(path) {
        return this.request('DELETE', path);
    }
};

var User = Backbone.Model.extend({
    sync: function(method, model, options) {
        var path = model.isNew() ? '/users' : '/users/' + model.id;
        
        switch (method) {
            case 'read':
                return APIClient.get(path);
            case 'create':
                return APIClient.post('/users', model.toJSON());
            case 'update':
                return APIClient.put(path, model.toJSON());
            case 'patch':
                return APIClient.patch(path, options.attrs || model.changedAttributes());
            case 'delete':
                return APIClient.delete(path);
        }
    }
});

7.2 分页API #

javascript
var PagedCollection = Backbone.Collection.extend({
    initialize: function(models, options) {
        this.page = 1;
        this.perPage = options && options.perPage || 20;
        this.total = 0;
    },
    
    fetchPage: function(page, options) {
        options = options || {};
        options.data = _.extend(options.data || {}, {
            page: page,
            per_page: this.perPage
        });
        
        var self = this;
        return this.fetch(options).done(function() {
            self.page = page;
        });
    },
    
    parse: function(response) {
        this.total = response.total;
        this.totalPages = response.total_pages;
        return response.data;
    },
    
    hasNextPage: function() {
        return this.page < this.totalPages;
    },
    
    hasPrevPage: function() {
        return this.page > 1;
    },
    
    nextPage: function() {
        if (this.hasNextPage()) {
            return this.fetchPage(this.page + 1);
        }
    },
    
    prevPage: function() {
        if (this.hasPrevPage()) {
            return this.fetchPage(this.page - 1);
        }
    }
});

八、总结 #

8.1 URL配置 #

属性/方法 说明
urlRoot 模型URL根路径
url 集合URL
url() 动态生成URL

8.2 最佳实践 #

  1. 使用RESTful API设计
  2. 合理配置URL
  3. 使用parse处理响应
  4. 统一错误处理
  5. 封装API客户端
最后更新:2026-03-28