1

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

简而言之,这里有一些事情:

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 的结束 CORS 请求的日志

据我了解,这就是我所需要的一切。这很有趣,因为 $.ajax.beforeSend 函数确实被触发了,但是根本没有实际 GET 触发的日志(服务器什么也没收到)。它似乎在它被调用之前就被取消了。错误回调确实触发,并打印:

SyntaxError: JSON.parse: unexpected end of data

JSON.parse 可能是因为当错误到达时我期望服务器提供 JSON 以在我的应用程序上将它们显示给最终用户,但是,除了该消息之外,我没有得到任何关于正在发生的事情或原因的线索请求被取消。

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

任何关于正在发生的事情的想法将不胜感激。

4

1 回答 1

3

所以,这是一段坎坷的旅程,但我最近学到了很多关于 CORS 之类的东西,我想我不妨回答这个问题,让子孙后代意识到。希望这会帮助那里的人。

首先,一点背景:

这是一个简单的形式,它被发送到另一个域上的服务器,因此是 CORS 位。我正在使用 Backbone 来处理客户端、服务器上的 Rails 和 HAL 来管理周围的数据。CORS 使用 Basic Auth 处理。收到数据后,服务器会以 201 - Created、一个空的正文和一个我应该下一步去的位置响应标头进行响应。

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

基本认证

当@j03w 提到这可能不是前端故障,而是后端时,他就在现场。确实,导致我的请求失败的原因是预检请求中缺少标头。事实证明,如果此预检失败,则不会显示任何请求,即使在技术上发生了一个请求。这个漂亮的宝石很快就解决了。

不过,这还没有结束。

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

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

起初,我认为这是 Backbone 的错,因为它期望更新的模型作为响应。事实证明,这不是 Backbone 的错。它仍然期待来自服务器的更新模型,但这不是触发错误回调的原因。这实际上是 jQuery 的错。从 1.9 开始,为 JSON 数据返回的空字符串被认为是格式错误的 JSON(因为它是)

但是服务器不想返回模型!(我是一名前端开发人员,但我假设后端人员有一个非常好的理由不这样做)。我该如何解决这个问题?

好吧,事实证明更改状态代码解决了这个问题。代替“201 - Created”,使用“204 - No Content”。jQuery 完美地处理了这个问题,现在调用了成功回调。

搬家的问题

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

除了那没有用。经过一些调试(在简单的 console.log(xhr.getAllResponseHeaders() 之前尝试了所有内容)之后,我发现它没有返回 Location 标头。实际上,它只返回了 2 个无用的标头:Cache-Control 和 Content-类型!我不需要这个!我把头转向我的萤火虫控制台上的 POST 请求的响应,它就在那里。位置标头正在发送,还有很多 xhr.getAllResponseHeaders() 的响应标头没有返回!为什么,以 triforce 的名义,这不起作用?

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

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

所以你有它。这是一段漫长而坎坷的旅程,但我希望这能帮助人们在处理所有这些技术时更快地解决问题。

于 2013-10-24T18:37:16.430 回答