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

评论

最快的解决方法肯定是仅对CORS响应使用Access-Control-Max-Age,因此仅被请求一次。

#1 楼

编辑2018-09-13:在此预检请求以及在此响应结束时如何避免它方面增加了一些精度。

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

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

某些浏览器发出此预检请求是一种安全措施,以确保服务器信任正在执行的请求。
表示服务器可以理解,根据请求发送的方法,来源和标头可以安全地执行。

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

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

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

Access-Control-Allow-Origin: *

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

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

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




edit 2018-09-13

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


某些请求不会触发CORS预检。尽管Fetch规范(定义了CORS)未使用该术语,但在本文中将其称为“简单请求”。不会触发CORS预检的请求即所谓的“简单请求”是满足以下所有条件的请求:

唯一允许的方法是:


GET
HEAD
POST

除了由用户代理自动设置的标头(例如,Connection,User-Agent或在Fetch规范中定义为“禁止标头名”的任何其他标头)外,仅允许将标头手动设置的是Fetch规范定义为“ CORS安全列出的请求标头”的内容,它们是:


Accept
Accept-Language
Content-语言
内容类型(但请注意以下附加要求)
DPR
下行链接
保存数据
视口宽度
宽度

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


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

没有在请求中使用的任何XMLHttpRequestUpload对象上注册事件侦听器;可使用XMLHttpRequest.upload属性访问这些对象。

请求中未使用ReadableStream对象。


评论


但是将Chrome标志设置给所有普通用户是不现实的。

–陈谦
2015年5月11日在7:41

说在进行跨源请求时需要预检请求是不正确的。仅在特定情况下才需要进行预检请求,例如,如果要设置自定义标题,或者发出除get,head和post之外的其他请求。

–罗宾·霍纳(Robin Clowers)
16年5月27日在16:54

有趣的是,当使用jQuery发出CORS请求时,JavaScript库特别避免设置自定义标头,并警告开发人员:对于跨域请求,我们认为预检条件类似于拼图游戏,我们根本就不用确定。

–雷达下方
16-10-17在15:01

如果我对它起作用的api进行卷曲,但是从chrome运行时会出现错误呢?

– SuperUberDuper
17年9月9日在10:02

@SuperUberDuper,因为CORS和预检请求与浏览器有关。您可以通过在请求中添加Origin标头来模拟CORS,以模拟请求,就像请求来自特定主机一样(例如yourwebsite.com)。您还可以通过将请求的HTTP方法设置为OPTIONS和Access-Control- *标头来模拟飞行前请求

– Leo Correa
17年9月9日在15:08

#2 楼

已经解决了这个问题,下面是我对该问题及其解决方案的总结。

根据CORS策略(强烈建议您阅读此内容),您不能仅仅强制浏览器停止发送OPTIONS要求是否认为必要。

有两种方法可以解决该问题:


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

简单请求

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

唯一允许的方法是:


GET
HEAD
POST

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


Accept
Accept-Language
Content-Language
Content-Type

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


application / x-www-form-urlencoded
multipart / form-data
文本/纯文本

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

设置OPTIONS检查的缓存

您可以设置一个用于OPTIONS请求的Access-Control-Max-Age,这样它才会再次检查权限,直到



Access-Control-Max-Age给出以秒为单位的值,该值表示可以在不发送其他预检请求的情况下将对预检请求的响应进行缓存的时间。 >

限制条件



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


>
Access-Control-Max-Age每次仅适用于一种资源,例如,具有相同URL路径但不同查询的GET请求将被视为不同资源。因此,对第二个资源的请求仍将触发预检请求。


评论


是的...这应该是公认的答案,并且与问题最相关。

–拉杰什Mbm
18 Mar 30 '18 at 12:17

感谢您提及Access-Control-Max-Age。这就是关键。它可以帮助您避免过多的预检请求。

–艾德里斯·莫克塔扎达(Idris Mokhtarzada)
18年4月26日在3:42

我正在使用axios调用获取请求。我在哪里可以在axios请求中设置Access-Control-Max-Age?

– Mohit Shah
18/09/5在10:04



+1 Access-Control-Max-Age标头是此处的关键。这应该是公认的答案!我在标头上设置了86400秒(24小时),偏好设置请求消失了!

– revobtz
18/09/15在20:04



@VitalyZdanevich不!不要仅仅因为它使您的请求变得“不简单”(从而触发CORS)而避免使用application / json。浏览器正在执行其工作。将您的服务器设置为返回诸如Access-Control-Max-Age:86400的标头,浏览器将在24小时内不重新发送OPTIONS请求。

– colm.anseo
19 Mar 10 '19 at 0:45

#3 楼

请针对实际的预检OPTIONS请求参考此答案:CORS-引入预检请求的动机是什么?

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


请求未设置自定义HTTP标头,例如'application / xml'或'application / json'等。
request方法必须为GET,HEAD或POST之一。如果是POST,则内容类型应为application/x-www-form-urlencodedmultipart/form-datatext/plain中的一种


参考:
https://developer.mozilla.org/zh-CN/docs/Web / HTTP / Access_control_CORS

评论


为“自定义HTTP标头” +1!以我为例,它们导致触发飞行前请求。我重构了请求以发送我在标头中发送的任何内容,因为请求正文和OPTIONS请求停止发送。

–安德烈
16年2月4日在16:28

application / xml或application / json不是“自定义HTTP标头”。标头本身将是Content-Type,并且将标头称为“自定义”将产生误导。

– Leo Correa
17年1月29日在15:40

删除了自定义HTTP标头,这就像一个魅力!

– Tim D
17年5月11日在19:47

#4 楼

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

评论


哦,我在想什么。调试了几个小时,这是我的解决方案。缓存由于调试控制台而被禁用。

–毛里斯
16-10-22在18:04

即使关闭了调试控制台,也会发送预检请求

–路卡·佩里科(Luca Perico)
18年6月1日在16:09

Luca:是的,但是重点是,关闭开发工具时,“禁用缓存”无效。如果未禁用缓存,则预检请求仅发送一次(当然,每个服务器)(如果禁用了缓存,则在每个请求之前发送)。

–尼尔
18 Jun 3'在9:42



那真的很有帮助。

– Anuraag Patil
19-10-27在18:15

#5 楼

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

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

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

评论


这实际上是一种嵌入式代理吗?

– Matanster
16年7月14日在12:43

“选项请求是将任何数据发送(发布)到另一个域时的预检请求。” - 这不是真的。您可以使用XHR发送可以使用普通HTML格式发送的任何POST请求,而无需触发预检请求。仅当您开始执行表单无法执行的操作(例如自定义内容类型或额外的请求标头)时,才发送预检。

–昆汀
16年2月2日在6:53

这里的解决方案似乎依赖于iframe填充程序,该填充程序在某些情况下可以工作,但有一些主要限制。如果您想知道响应的HTTP状态代码或另一个HTTP响应标头的值,会发生什么?

–斯蒂芬·克罗斯比(Stephen Crosby)
17 Mar 22 '17 at 21:25

iFrame并非为此而设计。

– Romko
18年1月30日在13:37

这是一个软件实现,并且确实回答了最后一个问题:“是否有任何方法可以完全阻止浏览器发送OPTIONS请求?”。长话短说,在Mozilla或Chromium中无法禁用它,它是在代码中实现的,唯一的“工作”选项只是在规避这种行为。

–清道夫
19年6月5日在17:51



#6 楼

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

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

在Chrome中禁用同一来源策略

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

Safari:在Safari中禁用同源策略


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


评论


您应该在标有“这绝不是永久解决方案!!!”的部分上加亮点。相同来源策略是一种非常重要的浏览器安全措施,在正常浏览Internet时切勿禁用该策略。

–詹尼斯
17年7月7日在8:11

我希望网络能够以这种方式工作,因此我们可以轻松地从服务器请求所需的数据。

–jemiloii
17年11月14日在15:45

#7 楼

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

让服务器以Access-Control-Max-Age标头答复并发送给相同的端点,预检请求将被缓存并且不再发生。

评论


这次真是万分感谢!在我阅读的所有CORS文档中,使用此标头缓存OPTIONS请求的事实都是非常不透明的。

– joshperry
17年6月30日在21:25

并且缓存仅在完全相同的网址下生效。我想要一个域级别的预检缓存,它可以真正减少往返次数。 (CORS很傻!)

–奇迹
18年5月4日在3:38



#8 楼

我已经解决了这个问题。

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


它仅用于开发。与此相关,我正在等待9ms和500ms,而不是8s和500ms。我可以这样做,因为生产JS应用程序将与生产设备位于同一台机器上,因此不会出现OPTIONS,但是开发是我的本机。

#9 楼

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

评论


如果您做的事情不简单,您只会收到OPTIONS请求。您只能使用JSONP发出简单的请求(GET,无自定义标头,无身份验证数据),因此JSONP不能替代此处。

–昆汀
15年5月16日在16:36

是的,我知道这一点,但我不知道确切的项目要求。我知道这不是简单的避免cors,但这取决于项目。在最坏的情况下,为了避免CORS,您需要使用get参数传递数据。因此,JSONP可以根据项目要求替换cors(如您所说,使用简单的请求)

–乔斯·马托(Jose Mato)
15年5月16日在16:39

我不了解所谓的飞行前设计。如果客户端决定将数据发送到服务器,那会导致它不安全吗?我认为将电线上的负载增加一倍没有任何意义。

–陈谦
2015年5月16日16:55

@ElgsQianChen这可能可以回答您的问题stackoverflow.com/questions/15381105 / ...

– Leo Correa
2015年5月17日15:58

#10 楼

花了整整一天半的时间来解决类似的问题后,我发现它与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)。

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

#11 楼

对我有用的是导入“ 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始终会添加Content-Type标头,而该标头不在我以前的AllowedHeaders配置中。

#12 楼

我过去使用的一种解决方案-假设您的网站位于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网站上-您可以提出相同的原始请求,而无需任何选项请求:)

#13 楼

如果使用代理来拦截请求并写入适当的标头,则可以解决该问题。
在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;
}


}

#14 楼

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

如果找到一些时间,我将对其进行测试并更新此帖子!

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

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

评论


我刚刚测试过,但它不起作用,CSP要求xhr请求接纳后仍需要CORS ...

– Arnaud Tournier
18年7月26日在11:34