为什么我收到的是 OPTIONS 请求而不是 GET 请求?

329
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

它对该URL执行OPTIONS请求,但回调函数从未被传递任何内容。

当它不跨域时,它可以正常工作。

jQuery难道不应该使用 <script> 节点进行调用,并在加载后执行回调吗?我知道我无法获取结果(因为它是跨域的),但这没关系;我只想让调用通过。这是一个错误吗,还是我做错了什么?


2
可能是由于跨域问题导致的。例如,如果您在文件 File://PATH_TO_WEBSITE 上而不是使用 localhost/WEBSITE_LINK。 - James111
10个回答

303
根据MDN
预检请求
与简单请求不同(如上所述),“预检”请求首先向其他域上的资源发送HTTP OPTIONS请求标头,以确定实际请求是否安全。跨站点请求像这样进行预检,因为它们可能对用户数据产生影响。特别是,如果请求满足以下条件,则进行预检:
- 它使用除GET或POST之外的方法。此外,如果使用POST发送请求数据并且Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain之一,例如,如果POST请求使用application/xml或text/xml向服务器发送XML有效负载,则请求将被预检。 - 它在请求中设置自定义标头(例如,请求使用诸如X-PINGOTHER之类的标头)。

55
这个更改解决了我们的问题,将“application/json”改为“text/plain”停止了可怕的OPTIONS请求。 - Keeno
12
我不明白的是为什么浏览器会使用OPTIONS方法来检查要发送的实际请求是否安全。但是这是在什么意义上?我的意思是,服务器也可以通过某些响应头来限制请求,那么为什么还需要这个呢? - hardik
14
请记住,通过添加CORS,您可能会接受来自任何人的请求,并且他们可以通过请求(POST,PUT,DELETE等)操纵您服务器上的数据。在这些情况下,例如使用自定义标头时,浏览器只是首先与服务器检查服务器是否愿意接受该请求,然后再发送它,因为向服务器发送未经请求的请求可能对您的数据非常危险,而且如果服务器不想要这些潜在的大负载,那么浏览器发送它们的目的是什么,因此需要进行预检OPTIONS检查。 - davidnknight
9
如果向服务器发送您的数据可能存在风险,也就是说,服务器可能会被攻破,那么恶意的服务器当然会以“当然,请全部发送!”的方式响应您的OPTIONS请求。这如何确保安全?(诚恳的问题) - Matt
3
预检请求并不是一种安全措施,而是一种“不改变规则”的措施。-请见What is the Motivation Behind Introducing Preflight Requests的回答。 - FMJaguar
显示剩余4条评论


12

如果您正尝试进行 POST

请确保对表单数据进行 JSON.stringify 并作为 text/plain 发送。

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}

3

只需将“application/json”更改为“text/plain”,并不要忘记使用JSON.stringify(request):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });

2

我不相信jQuery会自然地在给定像那样的URL时执行JSONP请求。但是,当您告诉它要使用哪个参数作为回调时,它将执行JSONP请求:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

完全由接收脚本决定是否使用该参数(不一定要称为“jsoncallback”),因此在这种情况下,该函数将永远不会被调用。但是,既然您声明只想让metaward.com上的脚本执行,那么这样做就可以了。


我的回调函数仍然会被通知脚本元素已完全加载吗?我只是想确保更新发生在我查询API之前。 - Paul Tarjan
只有当接收脚本支持JSONP并愿意调用您指定的函数时,您才能知道它是否成功。如果该脚本仅生成一块没有其他行为的JSON数据,则无法确定何时完成加载。如果必须确定何时完成加载,您可以考虑在自己的服务器上实现一个代理脚本。 - VoteyDisciple

1
我遇到了同样的问题。我的解决方法是在我的PHP脚本中添加头文件,这些头文件仅在开发环境中存在。
这允许跨域请求:
header("Access-Control-Allow-Origin: *");

这段话告诉预检请求,客户端可以发送任何标头。
header("Access-Control-Allow-Headers: *");

这样就不需要修改请求。
如果您的开发数据库中有可能泄露敏感数据,那么您可能会三思而后行。

这个要添加到 PHP 文件的哪个位置? - RGriffiths
我相信只要确保它对于每个传入的请求都执行,你可以将它放置在 PHP 代码的任何位置。 - fivebit

1
在我的情况下,问题与CORS无关,因为我正在向同一Web服务器发出jQuery POST请求。数据是JSON,但我省略了dataType:'json'参数。
我没有(也没有添加)contentType参数,如David Lopes的答案所示。

1
事实上,由于安全原因(考虑从客户端获取“受限制”的网页并将其发送回服务器,这将是一个安全问题),不允许跨域AJAX(XMLHttp)请求。
唯一的解决方法是使用回调。即创建一个新的脚本对象,并将src指向端点JavaScript,该JavaScript具有JSON值的回调函数(myFunction({data}),myFunction是对数据执行某些操作的函数(例如,在变量中存储它)。

1
没问题,但我可以在<script src="">或<img src="">中加载它,浏览器会愉快地访问它。我只想知道何时完全加载,以便可以查询导入结果。 - Paul Tarjan

1
我能够在下面的 header 的帮助下解决它。
Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

如果您使用的是Nodejs,则可以复制/粘贴以下代码。
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});

0

看起来Firefox和Opera(在Mac上测试过)不喜欢这个跨域的问题(但Safari可以正常工作)。

您可能需要调用本地服务器端代码来curl远程页面。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接