乐趣区

关于前端:理解跨域cors

这篇文章次要解说在开发过程中,前后端是如何解决跨域问题的,对于跨源资源共享的理论知识能够通过浏览 MDN 跨源资源共享(CORS)的文章理解。

解决跨域的伎俩有很多,这里次要是通过后端 nodejs来做示例。

筹备工作

咱们晓得同源策略约定 协定 域名 端口 三者雷同能力拜访到资源,这里通过 nodejs 来发明一个不同端口的跨域申请环境。

api 接口服务

  • api.js
// api.js
const handleRequest = (req, res) => {res.end(JSON.stringify({result:"success"})
)}

require("http").createServer(handleRequest).listen(3000)

前端服务

前端服务 index.htmlserver.js

  • server.js
  • index.html
// server.js
const 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 apinode 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 的话,除了 getpost申请,肯定还会有 deleteput等申请,咱们批改 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-Credentialstrue时,能力获取到 cookies 信息。

// index.html
request.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.js
const 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>
退出移动版