前端如何解决跨域问题?( 二 )


看到这里你可能有疑问为什么上面的示例没有预检请求?因为 CORS 将请求分为了两类:简单请求和非简单请求 。我们上面的情况属于简单请求,所以也就没有了预检请求 。
让我们继续在看下简单请求和非简单请求是如何定义的 。
预检请求定义根据 MDN 的文档定义,请求方法为:GET、POST、HEAD,请求头 Content-Type 为:text/plain、multipart/form-data、
Application/x-www-form-urlencoded 的就属于 “简单请求” 不会触发 CORS 预检请求 。
例如,如果请求头的 Content-Type 为 application/json 就会触发 CORS 预检请求,这里也会称为 “非简单请求” 。
“MDN 文档
developer.mozilla.org/en-US/docs/Web/HTTP/CORS 简单请求”[2] 有更多关于简单请求的字段定义 。
预检请求示例通过一个示例学习下预检请求 。
设置客户端为 index.html 里的 fetch 方法增加一些设置,设置请求的方法为 PUT,请求头增加一个自定义字段 Test-Cors 。
<script>fetch('http://127.0.0.1:3011/api/data', {method: 'PUT',headers: {'Content-Type': 'text/plain','Test-Cors': 'abc'}}); </script> 上述代码在浏览器执行时会发现是一个非简单请求,就会先执行一个预检请求,Request Headers 会有如下信息:
OPTIONS /api/data HTTP/1.1 Host: 127.0.0.1:3011 Access-Control-Request-Method: PUT Access-Control-Request-Headers: content-type,test-cors Origin: http://127.0.0.1:3010 Sec-Fetch-Mode: cors 可以看到有一个 OPTIONS 是预检请求使用的方法,该方法是在 HTTP/1.1 协议中所定义的,还有一个重要的字段 Origin 表示请求来自哪个源,服务端则可以根据这个字段判断是否是合法的请求源,例如 Websocket 中因为没有了同源策略限制,服务端可以根据这个字段来判断 。
Access-Control-Request-Method 告诉服务器,实际请求将使用 PUT 方法 。
Access-Control-Request-Headers 告诉服务器,实际请求将使用两个头部字段 content-type,test-cors 。这里如果 content-type 指定的为简单请求中的几个值,Access-Control-Request-Headers 在告诉服务器时,实际请求将只有 test-cors 这一个头部字段 。
设置服务端上面讲解了客户端的设置,同样的要使请求能够正常响应,还需服务端的支持 。
修改我们的 server.js 重点是设置 Response Headers 代码如下所示:
res.writeHead(200, {'Access-Control-Allow-Origin': 'http://127.0.0.1:3010','Access-Control-Allow-Headers': 'Test-CORS, Content-Type','Access-Control-Allow-Methods': 'PUT,DELETE','Access-Control-Max-Age': 86400 }); 为什么是以上配置?首先预检请求时,浏览器给了服务器几个重要的信息 Origin、Method 为 PUT、Headers 为 content-type,test-cors 服务端在收到之后,也要做些设置,给予回应 。
Access-Control-Allow-Origin 表示 “http://127.0.0.1:3010” 这个请求源是可以访问的,该字段也可以设置为 “*” 表示允许任意跨源请求 。
Access-Control-Allow-Methods 表示服务器允许客户端使用 PUT、DELETE 方法发起请求,可以一次设置多个,表示服务器所支持的所有跨域方法,而不单是当前请求那个方法,这样好处是为了避免多次预检请求 。
Access-Control-Allow-Headers 表示服务器允许请求中携带 Test-CORS、Content-Type 字段,也可以设置多个 。
Access-Control-Max-Age 表示该响应的有效期,单位为秒 。在有效时间内,浏览器无须为同一请求再次发起预检请求 。还有一点需要注意,该值要小于浏览器自身维护的最大有效时间,否则是无效的 。
看下增加了预检请求的效果,第一次先发出了 OPTIONS 请求,并且在请求头设置了本次请求的方法和 Headers 信息,服务端在 Response 也做了回应,在 OPTIONS 成功之后,浏览器紧跟着才发起了我们本次需要的真实请求,如图右侧所示 Resquest Method 为 PUT 。

前端如何解决跨域问题?

文章插图
 
本节代码示例:
github.com/qufei1993/http-protocol/tree/master/example/cors/02 CORS 与认证对于跨域的 XMLHttpRequest 或 Fetch 请求,浏览器是不会发送身份凭证信息的 。例如我们要在跨域请求中发送 Cookie 信息,就要做些设置:
为了能看到效果,我先自定义了一个 cookie 信息 id=NodejsRoadmap 。
重点是设置认证字段,本文中 fetch 示例设置 credentials: "include" 如果是 XMLHttpRequest 则设置 withCredentials:"include"
<body><script>document.cookie = `id=NodejsRoadmap`;fetch('http://127.0.0.1:3011/api/data', {method: 'PUT',headers: {'Content-Type': 'application/json','Test-Cors': 'abc',},credentials: "include"});</script> </body>


推荐阅读