这篇文章次要解说在开发过程中,前后端是如何解决跨域问题的,对于跨源资源共享的理论知识能够通过浏览MDN 跨源资源共享(CORS)的文章理解。
解决跨域的伎俩有很多,这里次要是通过后端 nodejs
来做示例。
筹备工作
咱们晓得同源策略约定协定
、域名
、端口
三者雷同能力拜访到资源,这里通过nodejs来发明一个不同端口的跨域申请环境。
api接口服务
- api.js
// api.jsconst handleRequest = (req, res) => { res.end(JSON.stringify({result:"success"}))}require("http").createServer(handleRequest).listen(3000)
前端服务
前端服务index.html
和server.js
- server.js
- index.html
// server.jsconst fs = require("fs")const path = require("path")const handleRequest=(req,res)=>{ const indexpath = path.resolve(__dirname, "./index.html"); fs.createReadStream(indexpath).pipe(res)};require("http").createServer(handleRequest).listen(8080)
//index.html<html><body><script> const request = new XMLHttpRequest(); request.responseType="json"; request.open("GET","http://127.0.0.1:3000",true); request.onload = ()=>{ console.log(request.response); } request.send();</script></body></html>
启动服务
在nodejs
环境下执行node api
和node server
,关上浏览器,输出http://127.0.0.1:8080
,在控制台能够查看到报错信息:
Access to XMLHttpRequest at 'http://127.0.0.1:3000/' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Access-Control-Allow-Origin
解决跨域第一步是响应头设置Access-Control-Allow-Origin
:
Access-Control-Allow-Origin: <origin> | *
origin
参数的值指定了容许拜访该资源的外域 URI,通配符 *
示意容许所有,批改api.js
如下:
const handleRequest = (req, res) => { res.setHeader("Access-Control-Allow-Origin", "*") res.end(JSON.stringify({result:"success"}))}
重新启动node api
,刷新8080
的地址就能够看到跨域拜访的资源了
Access-Control-Allow-Methods
我的项目中应用RESTful API
的话,除了get
和post
申请,肯定还会有delete
和put
等申请,咱们批改index.html
,换成delete
申请:
request.open("DELETE","http://127.0.0.1:3000",true);
关上浏览器后会发现,控制台又呈现了跨域的报错信息:
Access to XMLHttpRequest at 'http://127.0.0.1:3000/' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
批改api.js
,增加上Access-Control-Allow-Methods
属性能够解决这个问题:
res.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS")
Access-Control-Max-Age
在浏览器控制台Network
中,能够看到每次发送的XHR
申请都带上一个options
申请,这是因为浏览器必须首先应用 OPTIONS
办法发动一个预检申请(preflight request),从而获知服务端是否容许该跨源申请,咱们能够给响应头增加Access-Control-Max-Age
,防止每次都发送预检申请,限度options发送频率:
res.setHeader("Access-Control-Max-Age", 86400)
Access-Control-Max-Age
表明该响应的无效工夫为 86400 秒,也就是 24 小时。在无效工夫内,浏览器毋庸为同一申请再次发动预检申请。
请留神,浏览器本身保护了一个最大无效工夫,如果该首部字段的值超过了最大无效工夫,将不会失效。
Access-Control-Allow-Headers
在前后端的通信中,咱们常常会在申请头加上tokan
信息发送给服务端,批改index.html
如下:
request.open("POST","http://127.0.0.1:3000/get",true); request.responseType="json";+ request.setRequestHeader("Authorization","token"); request.onload = ()=>{ console.log(request.response); } request.send();
关上浏览器控制台,咱们会看到新的跨域报错信息:
Access to XMLHttpRequest at 'http://127.0.0.1:3000/get' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
批改api.js
,增加上Access-Control-Allow-Headers
,指明理论申请中容许携带的首部字段:
res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization")
Access-Control-Allow-Credentials
咱们在应用一些表单或者上传组件库的时候,常常能够看到withCredentials
这个属性的设置,因为在跨域的状况下设置的cookies
是不发送到服务器的,只有当后端响应头Access-Control-Allow-Credentials
为true
时,能力获取到cookies
信息。
// index.htmlrequest.withCredentials = true;document.cookie="name=chenwl";
res.setHeader("Access-Control-Allow-Credentials",true)
不过即使设置了Access-Control-Allow-Credentials
,这里后端还是无奈拿到cookies
信息,因为之前Access-Control-Allow-Origin
这里设置成了"*",这里要求 必须指定明确的、与申请网页统一的域名,批改Access-Control-Allow-Origin
如下:
res.setHeader("Access-Control-Allow-Origin", req.headers.origin)
通常状况下,咱们还会为Access-Control-Allow-Origin
设置白名单:
const corsWhitelist = [ "https://domain1.example", "https://domain2.example", "https://domain3.example",]if (corsWhitelist.indexOf(req.headers.origin) !== -1) { res.setHeader("Access-Control-Allow-Origin", req.headers.origin)}
残缺代码
api.jsconst handleRequest = (req, res) => { res.setHeader("Access-Control-Allow-Origin", req.headers.origin) res.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS") res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization") res.setHeader("Access-Control-Max-Age", 86400) res.setHeader("Access-Control-Allow-Credentials", true) res.end(JSON.stringify({ cookie: req.headers.cookie }))}require("http").createServer(handleRequest).listen(3000)
index.html<html><body><script> let request = new XMLHttpRequest(); request.open("POST","http://127.0.0.1:3000/get",true); request.responseType="json"; request.setRequestHeader("Authorization","tokena"); request.setRequestHeader("Content-Type","application/json;charsrt=utf-8"); request.withCredentials = true; document.cookie="name=chenwl"; request.onload = ()=>{console.log(request.response)} request.send();</script></body></html>