CORS与jQuery Dropzone问题并上传到Imgur

时间:2014-08-21 13:29:05

标签: javascript cors dropzone.js imgur

我尝试使用jQuery Dropzone将图像上传到Imgur或任何其他域,但这不起作用。

这是我的dropzone设置:

$("div.dropzone").dropzone
  success: -> console.log arguments
  paramName: "image"
  method: "post"
  maxFilesize: 2
  url: "https://api.imgur.com/3/upload"
  headers:
    Authorization: "Client-ID *************"

这不起作用。它表示返回码为0.请求标头:

Host: api.imgur.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://my.opencubes.io
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,cache-control,x-requested-with
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

首先,你可以看到cient id没有出现:(。但是最大的问题是使用的方法是 OPTIONS 。响应头:

headers

当我尝试将文件上传到我的另一个域时,我遇到了同样的问题(dropzone位于子域中)

在控制台中我看到:

Une demande multi-origines (Cross-Origin Request) a été bloquée : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur https://api.imgur.com/3/upload. Ceci peut être corrigé en déplaçant la ressource sur le même domaine ou en activant CORS.

哪个可以翻译

  

多源请求被阻止:策略“Same origin”不允许查看https://api.imgur.com/3/upload中的远程资源。这可以通过在samin域上移动资源或启用CORS来修复。

2 个答案:

答案 0 :(得分:16)

OPTIONS请求是一个普通请求:这用于询问与CORS限制相关的权限。请查看this page以了解CORS如何在幕后工作。

在您的情况下,这是一个纯粹的CORS相关问题。 OPTIONS请求包含此标头:

Access-Control-Request-Headers: authorization,cache-control,x-requested-with

这意味着:我可以使用" 授权"," 缓存控制"和" x-requested-with "我的跨域AJAX请求中的标头?

您得到的答案如下:

Access-Control-Allow-Headers :"Authorization, Content-Type, Accept, X-Mashape-Authorization"

这意味着:您只能使用这些标题:"授权","内容类型","接受"和&# 34 X-Mashape-授权"

如您所见," 缓存控制"和" x-requested-with "在允许列表中未列出,导致浏览器拒绝该请求。

我来到2个测试代码示例,显示了这种行为:

示例1(工作)

var data = new FormData();
data.append('image', 'http://placehold.it/300x500');

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.imgur.com/3/upload', true);
xhr.setRequestHeader('Authorization', 'Client-ID xxxxxxxxxx');
xhr.send(data);

以下是运行此代码时发送的预检请求标头(如Firefox 30 devtools所示,并且我已删除不相关的标头,例如User-Agent,Accept ...):

以及相应的响应标题

  • access-control-allow-origin:" *"
  • 访问控制 - 允许 - 方法:" GET,PUT, POST ,删除,选项"
  • 访问控制允许标题:" 授权,内容类型,接受,X-Mashape-Authorization"

在这里,我们可以看到我们提示访问"授权" header,并且服务器正在接受此标头,使用POST方法和任何原始URL,因此满足CORS要求并且浏览器允许该请求。

示例2(不工作)

var data = new FormData();
data.append('image', 'http://placehold.it/300x500');

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.imgur.com/3/upload', true);
xhr.setRequestHeader('Authorization', 'Client-ID xxxxxxxxxx');
// the only difference with the previous code is this line
xhr.setRequestHeader('Cache-Control', 'no-cache');
xhr.send(data);

预检请求的标题:

预检响应的标题(与示例1中的相同):

  • access-control-allow-origin:" *"
  • Access-Control-Allow-Methods:" GET,PUT,POST,DELETE,OPTIONS"
  • Access-Control-Allow-Headers:"授权,内容类型,接受,X-Mashape-Authorization"

此处,"访问控制请求标头"标题提示访问" cache-control",服务器未提供,因此CORS要求不满意,请求被浏览器拒绝。

这是一个JSFiddle,引用了针对您的问题的不同工作和不工作的演示:http://jsfiddle.net/pomeh/Lfajnebh/。注意细节以了解发生了什么,几乎没有评论,但他们在这里强调代码中最棘手的部分。

作为奖励,我已经向DropZone的GitHub存储库发送了一个拉取请求,以解决此问题(https://github.com/enyo/dropzone/pull/685),该问题允许您通过DropZone删除预定义的标头。试一试:

var myDropzone = new Dropzone('.dropzone', {
    //...
    headers: {
        'Authorization': authorizationHeader,
        // remove Cache-Control and X-Requested-With
        // to be sent along with the request
        'Cache-Control': null,
        'X-Requested-With': null
    }
});

上面的代码应该与我的修补版本(https://github.com/pomeh/dropzone/commit/f0063db6e5697888582421865840258dec1ffdc1)一起使用,而上面的代码不应该:

var myDropzone = new Dropzone('.dropzone', {
    //...
    headers: {
        'Authorization': authorizationHeader,
        // remove Cache-Control and X-Requested-With
        // to be sent along with the request
    }
});

答案 1 :(得分:2)

您正在使用浏览器same-origin security policy。每个浏览器都有一个; "来源"基本上意味着"同一个网站"。我在example.com上的JavaScript可以在example.com上访问它喜欢的任何内容,但它不允许从demonst.com,example.net或api.example.com读取任何内容。他们来自不同的起源。 Here's a table of what counts as the same origin

没有它,我可以写一个网页来窃取你所有的Gmail和私人Facebook照片。我的恶意JavaScript会向gmail.com和facebook.com发出网络请求,找到指向您电子邮件的链接。照片,加载数据,然后将其发送到我自己的服务器。

但是某些服务(如API)旨在由其他服务访问。 CORS所在的地方 - 跨源资源共享。 Web服务可以使用CORS告诉浏览器允许从脚本访问它是正常的。如果要通过提交到自己的服务器来测试代码,请确保您的服务器为sending the required HTTP response headers

如果您要在本地开发,还必须确保从网络服务器进行测试 - 以http://开头的地址,而不是file://。该协议是原始协议的一部分,因此您无法从文件URL提交到http端点。


CORS有不同类型的请求。某些请求被视为简单请求,但其他请求 - 具有自定义标头的请求 - 需要" preflighting"。这意味着浏览器将向服务器发送一个请求,说'&34;这个请求对CORS是否正常?"在发送实际请求之前使用HTTP OPTIONS方法。任何带有自定义标题的请求都需要预检; 你的HTTP OPTIONS所在的 。 jQuery adds a custom X-Requested-With header to AJAX requests,所以即使你没有添加那些,你仍然会在实际POST之前看到Options请求。

从截图中看,Imgur似乎将允许您的HTTP POST方法。让我们继续弄清楚为什么不起作用。


我们正在使用Imgur image upload API endpoint。这有一个必需参数(image),如果我们只想匿名上传,我们需要的只是a registered app。上传方法允许我们向图片发送简单的URL以供上传,因此我们尝试向Imgur发出AJAX请求:

$.ajax
  success: (data) -> console.log data
  type: "POST"
  data: 
    image: "http://placehold.it/300x500"
  url: "https://api.imgur.com/3/upload"
  headers:
    Authorization: "Client-ID *************" # Don't forget to put your actual Client-ID here!

下一步是尝试使用Filereader API从表单中读取文件,然后发送。这是一个CoffeeScript提交处理程序:

$('#file-form').submit (ev) -> 
  ev.preventDefault()
  file = $('#file-form input[name=file]').get(0).files[0] 
  $.ajax
    success: (data) -> console.log data
    type: "POST"
    data: 
      image: file
    url: "https://api.imgur.com/3/upload"
    headers:
      Authorization: "Client-ID *************"

最后,我们可以尝试使用Dropzone来实现同样的目标:

$("div.dropzone").dropzone
  success: (file, response) -> 
    console.log file
    console.log response
  paramName: "image"
  method: "post"
  maxFilesize: 2
  url: "https://api.imgur.com/3/upload"
  headers:
    "Authorization": "Client-ID *************"

Dropzone success回调有两个参数:上传的文件和服务器的响应。你可能对后者最感兴趣;您可以使用Imgur sends back an id and a link parameter on success向用户显示新上传的图片。


有一个使用JavaScript available on Github here的Imgur API的示例项目。