为啥会有OPTIONS请求

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为啥会有OPTIONS请求相关的知识,希望对你有一定的参考价值。

参考技术A Options 请求,我所了解的就是在发起跨域的非简单请求时,会先发起一个 options 预检请求(浏览器是自动发起的),去检测目标服务器是支持跨域请求。

响应报文包含一个 Allow 首部字段,该字段的值表明了服务器支持的所有 HTTP 方法

在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。

Options 请求会携带几个关键的 Request Header

服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。

预检响应头 response header 的关键字段

此次 OPTIONS 请求返回了响应头的内容,但没有返回响应实体 response body 内容。

这个才是我们真正要发送的请求

跨域请求触发了浏览器自动发起 OPTIONS 请求。

跨域的非简单请求会触发预检请求

那么满足哪些条件的请求是非简单请求呢?

PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH

Accept/Accept-Language/Content-Language/Content-Type/DPR/Downlink/Save-Data/Viewport-Width/Width

application/x-www-form-urlencoded、multipart/form-data、text/plain

可见一旦达到触发条件,跨域请求便会一直发送 2 次请求,这样增加的请求数是否可优化呢?答案是可以,OPTIONS 预检请求的结果可以被缓存。

如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用 OPTIONS 请求进行检测。

OPTIONS 请求即预检请求,可用于检测服务器允许的 http 方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。

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

【中文标题】为啥我收到的是 OPTIONS 请求而不是 GET 请求?【英文标题】:Why am I getting an OPTIONS request instead of a GET request?为什么我收到的是 OPTIONS 请求而不是 GET 请求? 【发布时间】:2010-11-18 09:37:12 【问题描述】:
<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 不应该只使用&lt;script&gt; 节点进行调用,然后在加载时进行回调吗?我知道我将无法获得结果(因为它是跨域的),但没关系;我只是想让电话通过。这是一个错误,还是我做错了什么?

【问题讨论】:

可能是跨域的 cos。例如,如果您在文件 File://PATH_TO_WEBSITE 而不是使用 localhost/WEBSITE_LINK 【参考方案1】:

根据MDN,

预检请求

与简单的请求(上面讨论过)不同,“预检”请求首先 向另一个资源发送一个 HTTP OPTIONS 请求头 域,以确定实际请求是否安全 发送。跨站点请求是这样预检的,因为它们可能 对用户数据有影响。特别是,请求是 预检如果:

它使用 GET 或 POST 以外的方法。此外,如果 POST 用于发送 使用 Content-Type 以外的请求数据 application/x-www-form-urlencoded、multipart/form-data 或 text/plain, 例如如果 POST 请求使用 application/xml 或 text/xml,然后预检请求。 它在请求中设置自定义标头(例如,请求使用诸如 X-PINGOTHER)

【讨论】:

这解决了我们的问题,从“application/json”更改为“text/plain”停止了可怕的选项请求 我不明白为什么浏览器使用 OPTIONS 方法请求只是为了检查实际请求是否可以安全发送。但在什么意义上?我的意思是服务器也可以对某些响应标头进行限制,所以为什么需要这样做? @hardik 请记住,通过添加 CORS,您可能会接受来自任何人的请求,其中他们可以通过请求(POST、PUT、DELETE 等)操作您服务器上的数据。在这些情况下,例如使用自定义标头时,浏览器只是先与服务器检查服务器是否愿意在发送请求之前接受请求,因为向服务器发送未经请求的请求可能对您的数据非常危险,而且,什么是如果服务器不想接受它们,浏览器中的点发送可能很大的有效载荷,因此飞行前 OPTIONS 检查。 @davidnknight 如果将您的数据发送到服务器可能会很危险,这意味着服务器可能会受到威胁,那么恶意服务器当然会以“当然,全部发送!”来响应您的 OPTIONS 请求。 .那安全性如何? (诚​​实的问题) "预检请求不是安全问题。相反,它们是不改变规则的问题。" - 查看What is the Motivation Behind Introducing Preflight Requests的答案【参考方案2】:

选项来自http://www.w3.org/TR/cors/ 更多信息请参见http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

【讨论】:

【参考方案3】:

如果您尝试 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"
    );

【讨论】:

【参考方案4】:

我不相信 jQuery 在给定这样的 URL 时会自然地执行 JSONP 请求。但是,当您告诉它使用什么参数进行回调时,它会执行 JSONP 请求:

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

完全由接收脚本来使用那个参数(它不必被称为“jsoncallback”),所以在这种情况下这个函数永远不会被调用。但是,既然您说您只想执行 metaward.com 上的脚本,那就可以了。

【讨论】:

是否仍会通知我的回调脚本元素已完全加载?我只是想确保更新发生在我查询 API 之前。 如果接收脚本支持JSONP,并且愿意调用你识别的函数,你会的。如果脚本只生成一个没有其他行为的 JSON 数据块,那么您将无法判断它何时完成加载。如果必须知道何时完成加载,您可以考虑在您自己的服务器上实现一个充当代理的脚本。【参考方案5】:

事实上,出于安全原因,不允许跨域 AJAX (XMLHttp) 请求(考虑从客户端获取“受限”网页并将其发送回服务器——这将是一个安全问题) .

唯一的解决方法是回调。这是:创建一个新的脚本对象并将 src 指向端 JavaScript,这是一个带有 JSON 值的回调(myFunction(data),myFunction 是一个对数据执行某些操作的函数(例如,存储它)在变量中)。

【讨论】:

对,但我可以在 【参考方案6】:

只需将“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,
    );

【讨论】:

【参考方案7】:

我遇到了同样的问题。我的解决方法是向我的 PHP 脚本添加标题,这些标题仅在开发环境中存在。

这允许跨域请求:

header("Access-Control-Allow-Origin: *");

这告诉预检请求客户端可以发送它想要的任何标头:

header("Access-Control-Allow-Headers: *");

这样就不需要修改请求了。

如果您的开发数据库中有可能泄露的敏感数据,那么您可能会三思而后行。

【讨论】:

【参考方案8】:

就我而言,这个问题与 CORS 无关,因为我向同一个 Web 服务器发出了 jQuery POST。数据是 JSON,但我省略了 dataType: 'json' 参数。

我没有(也没有添加)一个 contentType 参数,如上面 David Lopes 的回答所示。

【讨论】:

【参考方案9】:

我能够在以下标题的帮助下修复它

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

【讨论】:

【参考方案10】:

看起来 Firefox 和 Opera(也在 mac 上测试过)不喜欢这种跨域性(但 Safari 很好)。

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

【讨论】:

以上是关于为啥会有OPTIONS请求的主要内容,如果未能解决你的问题,请参考以下文章

JQuery POST 请求转换为 OPTIONS。为啥?

为啥我的跨域 POST 请求预检了 OPTIONS 请求?

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

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

为啥 Fetch API 将第一个 PUT 请求作为 OPTIONS 发送

CORS:为啥我的浏览器不发送 OPTIONS 预检请求?