保存模型时如何强制执行 POST 请求?

How to force a POST request when saving a model?

我需要制作一个 POST 到服务器端 API。我必须将 id 密钥发送到服务器的请求正文中。

我使用 Backbone 模型。但是当我这样做时:

myModel.set("id", somevalue)    
myModel.save()

触发的网络请求是:URL/someValue [PUT]

Backbones 不执行 POST 而是执行 PUT 并将 id 附加到 url.

所以我只想在 Backbone 不注意的情况下将 id 密钥传递给服务器。

来自Backbone's doc:

Backbone is pre-configured to sync with a RESTful API.

[...]

The default sync handler maps CRUD to REST like so:

  • createPOST /collection
  • readGET /collection[/id]
  • updatePUT /collection/id
  • patchPATCH /collection/id
  • deleteDELETE /collection/id

新条目没有 ID,因此如果您在保存之前为模型提供 ID,Backbone 默认为 PUT 请求,因为它认为您要保存现有条目。

如何使用 id 发出 POST 请求?

选择以下解决方案之一。

坚持RESTfulAPI

这一个是显而易见的。可以的话,坚持标准。

更改 API 以处理 PUT/PATCH 请求并仅在创建时使用 POST。使 API 端点从 URL.

获取 ID

RESTful API best practices

传递type选项1

简单且非常适合一次性情况。

传递给 save(或 fetch)的每个选项都会覆盖 sync 函数默认定义的选项并传递给 jQuery.ajax function.

Backbone sync source

// Make the request, allowing the user to override any Ajax options.
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
var url = model.url(); // get the url before setting the `id`
model.save({ 
    id: somevalue 
}, { 
    url: url, // fix the url
    type: 'POST' // choose the HTTP verb
});

修复模型使用的url很简单,你也有一些选择:

重写 url 函数 (source) 适用于每次调用都应使用特定 url 而无需附加默认值 id 的情况。

var MyModel = Backbone.Model.extend({
    url: function() {
        return _.result(this, 'urlRoot') ||
            _.result(this.collection, 'url') ||
            urlError();
    }
});

在模型上设置idAttribute

这取决于您要传递的 id 在数据中的含义。

Backbone 模型使用 "id" 具有默认的 id 属性名称。您可以通过覆盖 idAttribute property of the model. Whatever the name, it is always automatically made available through the model.id property.

来指定不同的名称

现在,假设 id 属性与此模型无关并且此模型的真实 id 属性名称类似于 UID,您可以更改模型的 idAttribute反映属性的真实名称(或者它甚至可以是一个永远不会成为属性的字符串)。

var MyModel = Backbone.Model.extend({
    idAttribute: 'UID',
});

现在,id 属性不被视为当前模型的 id,model.isNew() 将 return true,发送一个 POST 请求在保存时创建它。

更改 sync/save 函数行为

如果您使用的 API 不是 而不是 RESTful,您可以通过覆盖使用的 sync function. This can be done on the model or collection, or on the Backbone.sync function 来调整行为默认情况下由集合和模型。

例如,如果你想让每个请求默认使用 POST MyModel class:

var MyModel = Backbone.Model.extend({
    sync: function(method, model, options) {
        return Backbone.sync.call(this, method, model,
            _.extend({ type: 'POST' }, options));
    }
});

您可以仅使用 save 函数来执行类似的操作,让 fetch 照常执行其 GET 请求。

使用emulateHTTP setting2

If you want to work with a legacy web server that doesn't support Backbone's default REST/HTTP approach, you may choose to turn on Backbone.emulateHTTP. Setting this option will fake PUT, PATCH and DELETE requests with a HTTP POST, setting the X-HTTP-Method-Override header with the true method.

[...]

Backbone.emulateHTTP = true;

model.save();  // POST to "/collection/id", with "_method=PUT" + header.

不要覆盖isNew

Has this model been saved to the server yet? If the model does not yet have an id, it is considered to be new.

该站点上的一些其他答案建议覆盖 isNew function不要。该函数有其目的,覆盖它以强制执行 POST 请求是一个糟糕的 hack,而不是解决方案。

isNew 仅供内部使用,但也可由您的代码或其他库和 Backbone 插件使用。


1 虽然我没有从堆栈溢出中获取它,但它已经是 an answer by Andrés Torres Marroquín on a similar question.

2 取自 Maanas Royy's answer.