CORS 跨域

鸣谢 https://juejin.im/post/5c0a55e76fb9a049ef2665ba

CORS 跨域资源共享

正常发送请求

正常情况下

前端网址为 http://127.0.0.1:58009 向后端http://localhost:8888/ 发送请求

// index.html
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888/')
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText)
    }
}
xhr.send()


// 后端 index.js
const http = require('http')
const PORT = 8888
const server = http.createServer((request, response) => {
    response.end("{name: 'quanquan', friend: 'guiling'}")
})
server.listen(PORT, () => {
  console.log('服务启动成功, 正在监听: ', PORT);
})

产生跨域错误

[外链图片转存失败(img-rqVyjhj2-1566464363210)(D:\2019.5月秋招准备\面试题\1.png)]

可以看到这个错误为 Access-Control-Allow-Origin 错误

所以接下来

我们可以在后端上设置

response.setHeader('Access-Control-Allow-Origin', *)

通配符* 表示允许所有域的请求;如果不想要任何域都可以访问,则可以指定特指域

如 response.setHeader(‘Access-Control-Allow-Origin’, ‘http://127.0.0.1:58009’ )

重新开启后端服务器,就会发现访问成功

[外链图片转存失败(img-iKG7k98R-1566464363212)(D:\2019.5月秋招准备\面试题\跨域图片\2.png)]

当请求为预检请求时

这里,有一个知识点

CORS分为简单请求和预检请求。

什么是简单请求呢?

  1. 请求为get,post,head的

  2. 手动设置的头部字段为

    Accept Accept-Language Content-Language Content-type DPR Downlink Save-data Viewpoer-Width Width

  3. Content-Type为以下三种类型之一

    1. application/ x-www-form-urlencoded
    2. multipart/form-data
    3. text/plain

如果不满足以上条件,则不是简单请求,就为非简单请求

什么是预检请求呢?

进行非简单请求时,浏览器会首先发出类型为OPTIONS的预检请求,请求地址相同。CORS服务端对预检请求作处理,预检请求使用的方法时OPTIONS,用于检查服务器是否支持CORS,以判断实际请求发送是否安全


现在,我们来尝试发送PUT请求,即不是简单请求

// index.html
xhr.open('PUT', 'http://localhost:8888/')

报错

[外链图片转存失败(img-byBtE49k-1566464363214)(D:\2019.5月秋招准备\面试题\跨域图片\3.png)]

Method PUT is not allowed by Access-Control-Allow-Methods

由此,我们可以想到解决办法

// index.js
response.setHeader('Access-Control-Allow-Method, 'PUT')

… 请求成功

携带自定义请求首部字段token

后端通过以下方法允许前端请求时首部可以携带自定义字段

response.setHeader('Access-Control-Allow-Headers', 'token')

前端怎么携带自定义字段呢?

xhr.setRequestHeader('token', 'quanquanbunengshuo')

通过以上方式,前端请求时Request Header上携带了字段为token,值为quanquanbunengshuo的

[外链图片转存失败(img-OOyRUfV6-1566464363215)(D:\2019.5月秋招准备\面试题\跨域图片\4.png)]

如何取到后端返回的token

后端

首先,后端需要设置响应头token

response.setHeader('token','quanquanlin')

前端通过 xhr.getAllResponseHeaders() 获取

console.log(xhr.getAllResponseHeaders())

启动服务器后,我们发现,console.log()只打印出了以下内容

[外链图片转存失败(img-NZKB5asR-1566464363216)(D:\2019.5月秋招准备\面试题\跨域图片\5.png)]

查看响应头

[外链图片转存失败(img-oyvwz1DY-1566464363218)(D:\2019.5月秋招准备\面试题\跨域图片\6.png)]

明明返回勒token

此时,在后端设置

response.setHeader('Access-Control-Expose-Headers', 'token')

这样,就可以console出来了

[外链图片转存失败(img-Pl9z27hY-1566464363218)(D:\2019.5月秋招准备\面试题\跨域图片\7.png)]

其他

1. 如果想要允许多个指定域名访问呢?

可以把允许的域名添加到数组里,,如下

const allowOrigin = ['http://127.0.0.1:58009', 'https://www.baidu.com']
  const {method, headers: {origin, cookie}} = request
  if (allowOrigin.includes(origin)) {
    response.setHeader('Access-Control-Allow-Origin', origin)
  }

2.能否控制预检请求发送两次请求呢?

通过设置Access-Control-Max-Age来控制预检请求的有效期。在指定的时间内再次跨域访问接口,不需要预检请求,单位是秒。

response.setHeader('Access-Control-Max-Age', 5)

3. 预检请求不返回内容

我们的响应结果本来应该是在正式的请求中才需要返回的, 但是我们看下预检请求的返回详情发现

全代码

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>CORS实现跨域</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="">
  </head>
  <body>
    <h3>CORS实现跨域</h3>
    <script>
      var xhr = new XMLHttpRequest()
      xhr.open('PUT', 'http://localhost:8888')
      xhr.setRequestHeader('token', 'quanquanbunengshuo')
      xhr.withCredentials = true
      xhr.onreadystatechange = function () { 
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.responseText)
          console.log(xhr.getAllResponseHeaders())
        }
      }
      xhr.send()
    </script>
  </body>
</html>

const http = require('http');

const PORT = 8888;

const allowOrigin = ['http://127.0.0.1:58009', 'https://www.baidu.com']
// 创建一个 http 服务
const server = http.createServer((request, response) => {
  const {method, headers: {origin, cookie}} = request
  if (allowOrigin.includes(origin)) {
    response.setHeader('Access-Control-Allow-Origin', '*')
  }
 
  response.setHeader('Access-Control-Allow-Methods', 'PUT')
  response.setHeader('Access-Control-Allow-Headers', 'token')
  response.setHeader('Access-Control-Max-Age', 5)
  // 允许前端请求携带cookie
  response.setHeader('Access-Control-Allow-Credentials', true)
  response.setHeader('token', 'quanquanlin')
  response.setHeader('Access-Control-Expose-Headers', 'token')
  if (method === 'OPTIONS') {
    response.writeHead(204);
    response.end('')
  } else if (!cookie) {
    response.setHeader('Set-Cookie', 'quanquanlin=fe')
  }

  response.end("{name: 'quanquan', friend: 'guiling'}")
  
});

// 启动服务, 监听端口
server.listen(PORT, () => {
  console.log('服务启动成功, 正在监听: ', PORT);
});

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值