【问题标题】:Ext.Ajax.request vs model.save cross origin cookiesExt.Ajax.request 与 model.save 跨域 cookie
【发布时间】:2020-03-18 08:52:18
【问题描述】:

我有一个问题,model.save 没有通过我的跨源请求发回 cookie,但Ext.Ajax.request 发回 cookie 没有问题。在我们的本地开发工作中,我们覆盖所有 Ajax 请求(包括模型操作)上的 beforerequest 以设置 withCredentials = trueuseDefaultXhrHeader = false,这有助于正常 Ext.Ajax.request 上的 CORS。

据我了解,拥有ajax 类型的代理类似于执行Ext.Ajax.request,这很明显,因为model.save 命中了之前的请求(您将在下面的示例中看到这一点)。您还会在示例中注意到 model.save 首先发送一个 OPTIONS 调用,然后再不跟进 POST(但这很可能是因为服务器)......无论哪种方式,OPTIONS 而不是 POST 似乎有点鱼腥味。

这是example

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把cookies发送到跨域?

【问题讨论】:

    标签: javascript cookies extjs cross-domain extjs6


    【解决方案1】:

    不,您无法绕过OPTIONS 请求。

    一旦您将有效负载添加到 POST 请求,浏览器将开始发送 OPTIONS 请求,就像 model.save() 所做的那样。如果您稍微更改它以匹配 model.save 操作,这也会发生在您的 Ext.Ajax.request 操作上:

    // 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。请求的资源上不存在“Access-Control-Allow-Origin”标头。 Origin '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 请求;否则,您将在浏览器控制台中找到上述错误消息。

    【讨论】:

    • 我不明白为什么我们不能让这个行为像一个普通的 Ajax 请求......它显然使用了一个普通的 Ajax 请求的一部分,那么究竟是什么让它表现得不同呢?是处理有效载荷的方式吗?我们不能更改模型的标题以作为表单数据发送吗?
    【解决方案2】:

    感谢 Sencha Forums 上的 Mitchell Simoens,他帮助我找出了问题的根本原因,即代理的作者。我需要创建一个自定义编写器来解决这个问题。

    Fiddle:

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

    【讨论】:

    • 我已尝试重现,并发送了一个 OPTIONS 请求:fiddle.sencha.com/#view/editor&fiddle/22psi.imgur.com/mctp6JO.png
    • 你没有完全复制它......如果你添加useDefaultXhrHeader = falsewithCredentials = true,它会起作用,但这里还有另一个问题,我正在努力解决它。 .. 这一切都归结为模型的编写者和修改它实际上将解决整个问题......当我有更好的解决方案时我会报告。
    猜你喜欢
    • 2012-02-10
    • 1970-01-01
    • 2011-03-21
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    相关资源
    最近更新 更多