跨域资源共享 CORS 实验

260 阅读2分钟

读了阮老师的文章跨域资源共享 CORS 详解 ,想要自己实验一下 CORS 参数的各种情况,进行了如下实验。

准备工作

使用 express 模拟提供接口的服务端,使用 webpack-dev-server 模拟页面访问的客服端。

跨域请求 - 报错

express服务端,监听3000端口,代码如下:

const express = require('express');
const app = express();

app.get('/cors', (req, res) => {
    res.send('get cors');
});

app.listen(3000, () => {
    console.log('app listening on port 3000');
});

webpack-dev-server客户端,监听9000端口,页面代码如下:

function cors(method) {
    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.response);
        }
    };

    xhr.onerror = function () {
        console.log('status: ' + xhr.status);
    };

    xhr.open(method, 'http://localhost:3000/cors');
    xhr.send();
} 

cors('GET');

启动两个项目后,访问页面http://localhost:9000,会看到如下错误:

浏览器出现了常见的跨域报错信息。

跨域请求 - 正常

修改服务端代码,添加 Access-Control-Allow-Origin 为 * 标识接收任意域名请求。

const express = require('express');
const app = express();

app.get('/cors', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.send('get cors');
});

app.listen(3000, () => {
    console.log('app listening on port 3000');
});

刷新浏览器,会看到目前请求正常。

跨域请求 - 发送Cookie

目前看到的请求中是不带 Cookie 的,如果跨域请求需要发送Cookie,需要进行如下设置。

修改服务器代码

const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());

app.get('/cors', (req, res) => {
    console.log(req.cookies);

    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:9000');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.send('get cors');
});

app.listen(3000, () => {
    console.log('app listening on port 3000');
});

修改客户端代码

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

这样在请求中就会带上Cookie了,注意这里在文章中提到不能设置 Access-Control-Allow-Origin 为 * ,必须指定明确的、与请求网页一致的域名。

跨域请求 - 非简单请求

非简单CORS请求,会在正式通信前,增加一次HTTP查询请求,称为"预检"请求(preflight)。预检请求用的请求方法是OPTIONS

修改客户端代码,通过PUT方法构造非简单请求

cors('PUT');

修改服务器代码

const express = require('express');
const app = express();

app.put('/cors', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.send('put cors');
});

app.listen(3000, () => {
  console.log('app listening on port 3000');
});

执行这样的代码会发现浏览器会发送两个请求,OPTIONS请求成功,但是PUT请求报错。

根据文章介绍,预检请求的响应头需要设置Access-Control-Allow-OriginAccess-Control-Allow-Methods等字段,表示允许CORS请求。

修改服务器代码

const express = require('express');
const app = express();

app.put('/cors', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.send('put cors');
});

app.options('/cors', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'PUT');
  res.send('options cors');
});

app.listen(3000, () => {
  console.log('app listening on port 3000');
});

执行代码可以看到OPTIONS和PUT请求都成功了。

实验结束。