我在model.save
没有通过跨源请求发送 cookie 时遇到了问题,但Ext.Ajax.request
发送回 cookie 没有问题。 在我们的本地开发中,我们覆盖所有 Ajax 请求(包括模型操作(上的 beforerequest 以设置withCredentials = true
和useDefaultXhrHeader = false
,这有助于在正常的 Ext.Ajax.request 上使用 CORS。
据我了解,拥有ajax
类型的代理类似于执行 Ext.Ajax.request,这很明显,因为 model.save 命中了 beforerequest(您将在下面的示例中看到这一点(。 您还会注意到,在示例中,model.save 首先发送一个 OPTIONS 调用,然后从不跟进 POST(但这很可能是因为服务器(......无论哪种方式,OPTIONS而不是POST似乎都有点可疑。
下面是示例:
Ext.Ajax.on({
beforerequest: function (conn, options, eOpts) {
console.log('here', conn, options)
options.useDefaultXhrHeader = false;
options.withCredentials = true;
}
});
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: ['test'],
proxy: {
type: 'ajax',
url: 'https://docs.sencha.com'
}
});
var model = Ext.create('MyModel');
// This will fire off an OPTIONS call and not send the cookies
model.save();
// This will fire off with the POST and send the cookies
Ext.Ajax.request({
url: 'https://docs.sencha.com',
method: 'POST'
});
有没有办法让 model.save 将 cookie 发送到跨域?
不,您无法绕过OPTIONS
请求。
一旦您向 POST 请求添加有效负载,浏览器就会开始发送OPTIONS
请求,就像model.save()
一样。如果您稍微更改Ext.Ajax.request
操作以匹配model.save
操作,也会发生这种情况:
// This will fire off an OPTIONS request.
Ext.Ajax.request({
url: 'https://docs.sencha.com',
method: 'POST',
jsonData: {id: "MyModel-1"}
});
此OPTIONS
请求是所谓的"预检请求",旨在从服务器获取服务器是否允许实际跨源请求的信息。服务器必须肯定地回答这个问题,包括包含一组 CORS 标头的 Http 响应标头。docs.sencha.com
确实应答了OPTIONS
请求,但它不会返回所需的标头,因此您会在浏览器控制台中找到一条错误消息:
XMLHttpRequest 无法加载 https://docs.sencha.com/?_dc=1499412133932。请求的资源上不存在"访问控制允许源"标头。因此,不允许访问源"https://fiddle.sencha.com"。
作为反例,我启用了 CORS 的后端向客户端发送以下标头:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, X-Requested-With
Origin
包含可能从中发送 CORS 请求的所有域,或 * 表示所有域。Methods
包含所有允许的方法,则至少需要OPTIONS, POST
.Headers
包含您的请求可能发送到服务器的所有标头。Chrome 控制台会告诉您是否缺少浏览器期望的;至少我是这样进入我的名单的。
如果在OPTIONS
请求中满足这些条件,则将发送实际的POST
请求;否则,您将在浏览器控制台中找到所述错误消息。
感谢 Mitchell Simoens 在 Sencha 论坛上,他帮助我找出了问题的根本原因,即代理的作者。 我需要创建一个自定义编写器来解决此问题。
小提琴:
Ext.define('Ux.data.writer.Json', {
extend : 'Ext.data.writer.Json',
alias : 'writer.ux-json',
config : {
/**
* @cfg {Boolean} useJsonData
* If {@link #allowSingle} is `true` and only one model is being
* saved, this will control if the request will set the data using
* `params` or `jsonData`.
*/
useJsonData : false
},
writeRecords: function(request, data) {
var me = this,
root = me.getRootProperty(),
transform = this.getTransform(),
json, single;
if (me.getExpandData()) {
data = me.getExpandedData(data);
}
if (me.getAllowSingle() && data.length === 1) {
// convert to single object format
data = data[0];
single = true;
}
if (transform) {
data = transform(data, request);
}
if (me.getEncode()) {
if (root) {
// sending as a param, need to encode
request.setParam(root, Ext.encode(data));
} else {
//<debug>
Ext.raise('Must specify a root when using encode');
//</debug>
}
} else if (single && !me.getUseJsonData()) {
request.setParams(Ext.apply(request.getParams(), data));
} else if (single || (data && data.length)) {
// send as jsonData
json = request.getJsonData() || {};
if (root) {
json[root] = data;
} else {
json = data;
}
request.setJsonData(json);
}
return request;
}
});
Ext.Ajax.on({
beforerequest: function (conn, options, eOpts) {
console.log(options.url, options);
options.useDefaultXhrHeader = false;
options.withCredentials = true;
}
});
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: ['blah'],
proxy: {
type: 'ajax',
url: 'https://docs.sencha.com',
writer: {
type: 'ux-json'
}
}
});
var model = Ext.create('MyModel', {
blah : 2
});
model.save();
Ext.Ajax.request({
url: 'https://docs.sencha.com',
method: 'POST',
params: {
blah: 1
}
});