骨干模型CORS提取未被发送

时间:2013-10-10 22:07:33

标签: jquery ajax backbone.js cors

我正在尝试使用来自另一个域的model.fetch(),但我似乎偶然发现了一堵墙。似乎我可以做跨域请求或设置自定义标头(无论哪个都没关系),而不是两者。不幸的是,我需要两者,因为我试图从中获取的API使用Basic Auth。

简而言之:

var Model = Backbone.Model.extend({
    url: function() {
        return a_cross_domain_url;
    },
    initialize: function() {
        this.fetch();
    }
});

Backbone.sync = _.wrap(Backbone.sync, function(sync, method, model, options) {
    if (!options.xhrFields) {
        options.xhrFields = {withCredentials:true};
    }

    options.headers = options.headers || {};

    // credentials is a string that looks like 'Basic d2Vwb3c6McriNzk3YZgvZTNkMTkzOGE4MTk3NjMwMDkzNmMwZGI='
    options.headers['Authorization'] = credentials;

    sync(method, model, options);
});

var m = new Model();

我抽象了很多代码,但我认为这是相关的信息。重要的是结束请求如下所示:

Firebug's log of end CORS request

据我了解,这就是我需要的全部工作。这很有趣,因为$ .ajax.beforeSend函数确实被触发了,但是根本没有实际GET的记录(服务器什么也没收到)。它甚至在被叫之前似乎被取消了。错误回调会触发,并打印:

SyntaxError: JSON.parse: unexpected end of data

JSON.parse可能是因为当我的应用程序出现错误并将其显示给最终用户时,我期待来自服务器的JSON,然而,除了该消息之外,我没有得到任何有关正在发生的事情的线索或者为什么请求被取消。

作为最后一点,如果我从请求中删除标头(在上面的js上),GET会被执行,但由于没有auth而被服务器拒绝。

对于所发生的事情的任何想法将不胜感激。

1 个答案:

答案 0 :(得分:3)

所以,这是一个坎坷的旅程,但我最近学到了很多关于CORS的知识,并且认为我可以回答这一点,以便后代去实现。希望这能帮助那些人。

首先,一点背景:

这是一个简单的表单,它被发送到另一个域的服务器,因此是CORS位。我使用Backbone来处理客户端,服务器上的Rails和HAL来管理数据。 CORS使用Basic Auth处理。收到数据后,服务器会响应201 - Created,一个空体和一个位置响应标题,我应该接下来。

这带来了很多啊哈的时刻,我即将与你分享。

Basic Auth

当他提到它可能不是前端故障,而是后端时,@ j03w就是现场。事实上,在预检请求中缺少标题使我的请求失败。事实证明,如果这次预检失败,即使技术上发生过,也不会显示任何请求。这很快通过this nifty gem解决了。

但这不是它的结束。

空的回应?你快要受苦了!

我现在能够与服务器进行交互,但是当表单发布时,响应是“201 - 创建”代码,这是一个成功代码,但未调用成功回调。在它的位置,错误接管了。为什么?好吧,事实证明我的服务器在响应正文中没有返回任何内容。这是有错的。

起初,我认为这是Backbone的错,因为它期待更新的模型作为响应。事实证明,这不是Backbone的错。它仍然期望从服务器更新模型,但它不是触发错误回调的原因。这实际上是jQuery的错。 As of 1.9, an empty string returned for JSON data is considered to be malformed JSON (because it is)

但服务器不想退回模型! (我是一名前端开发人员,但我认为后端人员有充分理由不这样做)。我该如何解决这个问题?

嗯,事实证明更改状态代码修复此问题。而不是“201 - 创建”,使用“204 - 无内容”。 jQuery处理完美,现在调用成功回调。

重新安置的问题

这个痛苦的长期旅程的最后一步是将用户重新定位到服务器在位置响应标头上发送的URL。 “非常简单!” - 说天真的开发者。 “我只需要使用xhr.getResponseHeader('Location')访问标题!” - 他兴奋地喊道。

除非没有效果。经过一些调试(在一个简单的console.log(xhr.getAllResponseHeaders())之前尝试了一切,我发现它没有返回Location头。事实上,它只返回2个无用的头:Cache-Control和Content-输入!我不需要这个!我把头转到我的firebug控制台上的POST请求的响应,就在那里。正在发送位置标题,以及许多响应头xhr.getAllResponseHeaders()没有回来!为什么,以三重奏的名义这不起作用?

嗯,事实证明它还需要更多配置。更多的服务器配置选项证明我们需要使用“Access-Control-Expose-Headers”明确告知客户端可以读取哪些头。列出“位置”解决了这个问题。

resource '*', { 
  headers: :any,
  expose:  ['Location'],
  methods: [:get, :post, :options]
}

所以你有它。这是一个漫长而颠簸的旅程,但我希望这可以帮助那些人在处理所有这些技术时更快地找到事情。