ChatGPT解决这个技术问题 Extra ChatGPT

为什么会发送 OPTIONS 请求,我可以禁用它吗?

我正在构建一个 Web API。我发现每当我使用 Chrome 发布、获取到我的 API 时,总是在真正的请求之前发送一个 OPTIONS 请求,这很烦人。目前,我让服务器忽略任何 OPTIONS 请求。现在我的问题是发送 OPTIONS 请求以使服务器负载加倍有什么好处?有什么方法可以完全阻止浏览器发送 OPTIONS 请求?

最快的解决方法肯定是在 CORS 响应上使用 Access-Control-Max-Age,这样它只会被请求一次。

R
Richard Downer

编辑 2018-09-13:添加了一些关于此飞行前请求的精确度以及如何在此响应结束时避免它。

OPTIONS 请求就是我们在 Cross-origin resource sharing (CORS) 中称为 pre-flight 的请求。

当您在特定情况下跨不同来源提出请求时,它们是必要的。

某些浏览器发出此飞行前请求作为一种安全措施,以确保正在完成的请求受到服务器的信任。这意味着服务器知道在请求上发送的方法、来源和标头是安全的。

每当您尝试执行跨源请求时,您的服务器不应忽略而是处理这些请求。

可以在此处找到一个很好的资源 http://enable-cors.org/

处理这些问题的一种方法是确保对于任何使用 OPTIONS 方法的路径,服务器都会发送带有此标头的响应

Access-Control-Allow-Origin: *

这将告诉浏览器服务器愿意回答来自任何来源的请求。

有关如何向服务器添加 CORS 支持的更多信息,请参阅以下流程图

http://www.html5rocks.com/static/images/cors_server_flowchart.png

https://i.stack.imgur.com/6jsKY.png

编辑 2018-09-13

CORS OPTIONS 请求仅在某些情况下触发,如 MDN docs 中所述:

有些请求不会触发 CORS 预检。这些在本文中被称为“简单请求”,尽管 Fetch 规范(定义 CORS)没有使用该术语。不触发 CORS 预检的请求——所谓的“简单请求”——是满足以下所有条件的请求: 唯一允许的方法是: GET HEAD POST 除了由用户代理自动设置的标头(例如例如,Connection、User-Agent 或任何其他在 Fetch 规范中定义为“禁止的标头名称”的标头),唯一允许手动设置的标头是 Fetch 规范定义为“CORS-safelisted request-header”,它们是: Accept-Language Content-Language Content-Type(但请注意下面的附加要求) DPR Downlink Save-Data Viewport-Width Width Content-Type 标头的唯一允许值是: application/x-www-form-urlencoded multipart/form-data text/plain 在请求中使用的任何 XMLHttpRequestUpload 对象上都没有注册事件监听器;这些是使用 XMLHttpRequest.upload 属性访问的。请求中没有使用 ReadableStream 对象。


但是将这个 Chrome 标志设置给所有普通用户是不现实的。
在进行跨源请求时说需要预检请求是不正确的。只有在特定情况下才需要预检请求,例如设置自定义标头或发出除 get、head 和 post 之外的请求。
有趣的是,当使用 jQuery 发出 CORS 请求时,JavaScript 库特别避免设置自定义标头,并警告开发人员:对于跨域请求,预检的条件类似于拼图游戏,我们根本就不要确定。
如果我对它工作的 api 执行 curl 怎么会,但是从 chrome 运行时出现错误?
@SuperUberDuper,因为 CORS 和预检请求是与浏览器相关的问题。您可以通过在请求中添加 Origin 标头来模拟 CORS,以模拟请求来自特定主机(例如 yourwebsite.com)。您还可以通过将请求的 HTTP 方法设置为 OPTIONSAccess-Control-* 标头来模拟预检请求
N
Neekey

已经经历过这个问题,下面是我对这个问题的结论和我的解决方案。

根据CORS strategy(强烈建议您阅读它),如果它认为需要,您不能只强制浏览器停止发送 OPTIONS 请求。

有两种方法可以解决它:

确保您的请求是“简单请求”为 OPTIONS 请求设置 Access-Control-Max-Age

简单的请求

一个简单的跨站点请求是满足以下所有条件的请求:

唯一允许的方法是:

得到

邮政

除了由用户代理自动设置的标头(例如 Connection、User-Agent 等)之外,唯一允许手动设置的标头是:

接受

接受语言

内容-语言

内容类型

Content-Type 标头的唯一允许值是:

应用程序/x-www-form-urlencoded

多部分/表单数据

文本/纯文本

一个简单的请求不会引起飞行前的 OPTIONS 请求。

为 OPTIONS 检查设置缓存

您可以为 OPTIONS 请求设置一个 Access-Control-Max-Age,这样它就不会再次检查权限,直到它过期。

Access-Control-Max-Age 以秒为单位给出对预检请求的响应可以缓存多长时间而不发送另一个预检请求的值。

注意到限制

对于 Chrome,Access-Control-Max-Age 的最大秒数为 600,即 10 分钟,根据 chrome 源代码

Access-Control-Max-Age 每次只对一个资源有效,例如,具有相同 URL 路径但不同查询的 GET 请求将被视为不同的资源。所以对第二个资源的请求仍然会触发一个预检请求。


是的......这应该是公认的答案,并且与问题最相关......!
感谢您提及Access-Control-Max-Age。这就是这里的关键。它可以帮助您避免过多的预检请求。
我正在使用 axios 来调用获取请求。我在哪里可以在 axios 请求中设置 Access-Control-Max-Age ?
+1 Access-Control-Max-Age 标头是这里的关键。这应该是公认的答案!我在标题上设置了 86400 秒(24 小时),并且 prefligth 请求消失了!
@VitalyZdanevich 不!不要仅仅因为它使您的请求不“简单”(从而触发 CORS)就避免使用 application/json。浏览器正在做它的工作。将您的服务器设置为返回类似 Access-Control-Max-Age: 86400 的标头,并且浏览器将不会在 24 小时内重新发送 OPTIONS 请求。
C
Community

请在实际需要预飞行选项请求时参考此答案:CORS - What is the motivation behind introducing preflight requests?

要禁用 OPTIONS 请求,ajax 请求必须满足以下条件:

请求未设置自定义 HTTP 标头,例如“application/xml”或“application/json”等请求方法必须是 GET、HEAD 或 POST 之一。如果是 POST,内容类型应该是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 之一

参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


为“自定义 HTTP 标头”+1!就我而言,他们导致触发飞行前请求。我重构了请求以发送我在标头中发送的任何内容,因为请求正文和 OPTIONS 请求停止发送。
application/xmlapplication/json 不是“自定义 HTTP 标头”。标头本身是 Content-Type 并且将该标头称为“自定义”会产生误导。
删除了自定义 HTTP 标头,这就像一个魅力!
@LeoCorrea 但是,当我为我完全删除这些标头时,不会发出 OPTIONS 请求。所以自定义在某种意义上是非常直观的,它意味着“用户提供”而不是“浏览器生成”
从字面上看,我一直在使用 Axios + Elasticsearch 调试 CORS。删除所有标题并简单地发布到 URL 工作。
N
Nir

当您打开调试控制台并打开 Disable Cache 选项时,将始终发送预检请求(即在每个请求之前)。如果您不禁用缓存,则只会发送一次飞行前请求(每台服务器)


哦,我在想什么。调试了几个小时,这是我的解决方案。由于调试控制台而禁用缓存。
即使调试控制台关闭,也会发送预检请求
Luca:是的,但关键是当开发工具关闭时,“禁用缓存”不起作用。如果未禁用缓存,则预检请求仅发送一次(当然是每个服务器),如果禁用缓存,则在每个请求之前发送。
那真的很有帮助。
c
codeforester

是的,可以避免选项请求。选项请求是您将任何数据发送(发布)到另一个域时的预检请求。这是浏览器安全问题。但我们可以使用另一种技术:iframe 传输层。我强烈建议您忘记任何 CORS 配置并使用现成的解决方案,它可以在任何地方工作。

看看这里:https://github.com/jpillora/xdomain

和工作示例:http://jpillora.com/xdomain/


这实际上是一种插入式代理吗?
“选项请求是您将任何数据发送(发布)到另一个域时的预检请求。” - 这不是真的。您可以使用 XHR 发送任何可以使用普通 HTML 表单发送的 POST 请求,而不会触发预检请求。只有当您开始执行表单无法执行的操作(例如自定义内容类型或额外的请求标头)时,才会发送预检。
这里的解决方案似乎依赖于 iframe shim,它在某些情况下有效,但有一些主要限制。如果您想知道响应的 HTTP 状态代码或另一个 HTTP 响应标头的值,会发生什么?
iFrame 不是为此而设计的。
这是一个软件实现,并且确实回答了最后一个问题:“有没有办法完全阻止浏览器发送 OPTIONS 请求?”。长话短说,没有办法在 Mozilla 或 Chromium 中禁用它,它在代码中实现,唯一的“工作”选项只是规避了这种行为。
C
Community

对于了解它存在的原因但需要访问在没有身份验证的情况下不处理 OPTIONS 调用的 API 的开发人员,我需要一个临时答案,以便我可以在本地开发,直到 API 所有者添加适当的 SPA CORS 支持或我获得代理 API启动并运行。

我发现您可以在 Mac 上的 Safari 和 Chrome 中禁用 CORS。

Disable same origin policy in Chrome

Chrome:退出 Chrome,打开终端并粘贴以下命令:open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

野生动物园:Disabling same-origin policy in Safari

如果你想在 Safari 上禁用同源策略(我有 9.1.1),那么你只需要启用开发者菜单,然后从开发菜单中选择“禁用跨域限制”。


您应该在“这绝不应该是永久解决方案!!!”的部分上加注更多的亮点。同源策略是一项非常重要的浏览器安全措施,在正常浏览互联网时切勿禁用。
我希望网络能够以这种方式工作,这样我们就可以从服务器请求我们需要的数据而无需额外的麻烦。
'--disable-web-security' 有效,谢谢。
注意,您现在需要使用 --user-data-dir="/some folder"
e
enpenax

如前文所述,OPTIONS 请求的存在是有原因的。如果您的服务器响应时间过长(例如海外连接)有问题,您也可以让浏览器缓存预检请求。

让您的服务器回复 Access-Control-Max-Age 标头,对于前往同一端点的请求,预检请求将被缓存并且不再发生。


这次真是万分感谢!在我阅读的所有 CORS 文档中,将使用此标头缓存 OPTIONS 请求的事实是非常不透明的。
并且缓存仅对完全相同的 url 生效。我想要一个可以真正减少往返的域级预检缓存。 (CORS 很傻!)
M
MKroeders

我已经解决了这个问题。

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}

它只是为了发展。有了这个,我正在等待 9 毫秒和 500 毫秒,而不是 8 秒和 500 毫秒。我可以这样做,因为生产 JS 应用程序将与生产在同一台机器上,所以不会有 OPTIONS,但开发是我本地的。


J
Jose Mato

你不能,但你可以避免使用 JSONP 的 CORS。


如果您正在做某事 not simple,您只会收到一个 OPTIONS 请求。您只能使用 JSONP 发出简单的请求(GET、没有自定义标头、没有身份验证数据),因此 JSONP 不能在这里替代。
是的,我知道,但我不知道确切的项目要求。我知道这不是简单的避免 cors,但它取决于项目。在最坏的情况下,要避免 CORS,您需要使用 get 参数传递数据。因此,JSONP 可以根据项目要求替换 cors(如您所说,使用简单请求)
我不明白所谓的飞行前的设计。如果客户端决定向服务器发送数据,什么会导致它不安全?我认为将电线上的负载加倍没有任何意义。
@ElgsQianChen 这可能可以回答您的问题stackoverflow.com/questions/15381105/…
D
David Schumann

在花了一整天半的时间试图解决一个类似的问题后,我发现它与 IIS 有关。

我的 Web API 项目设置如下:

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

我在 web.config > system.webServer 节点中没有 CORS 特定的配置选项,就像我在很多帖子中看到的那样

global.asax 或控制器中没有 CORS 特定代码作为装饰器

问题是应用程序池设置。

托管管道模式设置为经典(将其更改为集成),身份设置为网络服务(将其更改为 ApplicationPoolIdentity)

更改这些设置(并刷新应用程序池)为我修复了它。


t
themefield

OPTIONS 请求是网络浏览器的一个特性,所以要禁用它并不容易。但我找到了一种使用代理重定向它的方法。在服务端点还不能处理 CORS/OPTIONS、可能仍在开发中或配置错误的情况下,这很有用。

脚步:

使用选择的工具(nginx,YARP,...)为此类请求设置反向代理创建一个端点来处理 OPTIONS 请求。创建一个普通的空端点可能更容易,并确保它可以很好地处理 CORS。为代理配置两组规则。一种是将所有 OPTIONS 请求路由到上面的虚拟端点。另一个将所有其他请求路由到有问题的实际端点。更新网站以改用代理。

基本上这种方法是欺骗 OPTIONS 请求有效的浏览器。考虑到 CORS 不是为了增强安全性,而是为了放宽同源策略,我希望这个技巧能奏效一段时间。 :)


D
David McEleney

我过去使用过的一种解决方案 - 假设您的网站在 mydomain.com 上,您需要向 foreigndomain.com 发出 ajax 请求

配置从您的域到外部域的 IIS 重写 - 例如

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

在您的 mydomain.com 网站上 - 然后您可以发出相同的来源请求,并且不需要任何选项请求:)


R
Rafa Cuestas

如果使用代理拦截请求并写入适当的标头,则可以解决此问题。在 Varnish 的特殊情况下,这些将是规则:

if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
   set resp.http.Access-Control-Max-Age = "1728000";
   set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
   set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
   set resp.http.Content-Length = "0";
   set resp.http.Content-Type = "text/plain charset=UTF-8";
   set resp.status = 204;
}

}


K
Kurt

对我有用的是导入“github.com/gorilla/handlers”,然后以这种方式使用它:

router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")

headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))

一旦我执行了一个 Ajax POST 请求并将 JSON 数据附加到它,Chrome 总是会添加我之前的 AllowedHeaders 配置中没有的 Content-Type 标头。


A
Arnaud Tournier

也许有一个解决方案(但我没有测试它):您可以使用 CSP(内容安全策略)来启用您的远程域,并且浏览器可能会跳过 CORS OPTIONS 请求验证。

如果有时间,我会测试并更新这篇文章!

CSP:https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

CSP 规范:https://www.w3.org/TR/CSP/


我刚刚测试过,它不起作用,在 CSP 之后仍然需要 CORS 以进行 xhr 请求接纳...