2.8 跨域问题

跨域问题是前端开发中经常会碰到的问题,在很多前端面试中经常会问,跨域是什么?它解决了什么样的痛点?那么在讲述Koa实现跨域之前,简单介绍一下跨域的相关知识。

跨域问题通俗来讲,就是浏览器不能执行其他网站的脚本,这是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。所谓同源是指域名、协议、端口均相同。如果有一项不同,就不是同源,看下面几个例子。

  • http://www.123.com/index.html
    http://www.123.com/server.PHP
    只有路径不同,非跨域。
  • http://www.123.com/index.html
    http://www.456.com/server.php
    主域名不同:123/456,跨域。
  • http://abc.123.com/index.html
    http://def.123.com/server.php
    子域名不同:abc/def,跨域。
  • http://www.123.com:8080/index.html
    http://www.123.com:8081/server.php
    端口不同:8080/8081,跨域。
  • http://www.123.com/index.html
    https://www.123.com/server.php
    协议不同:http/https,跨域。

提示

localhost和127.0.0.1虽然都指向本机,但也属于跨域。

先来演示一下跨域会出现什么样的问题。现在有这样一个场景,一个Web服务是http://127.0.0.1:3000,要调用http://127.0.0.1:4000的接口,依据同源策略,这就是跨域调用。首先实现运行在服务端口号为3000的前端页面,代码如下。

<!——- static/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>跨域调用接口</title>
</head>
<body>
  <button onclick='getUserInfo()'>获取用户信息</button>
  <span id='data'></span>
</body>
<script>
  const getUserInfo = () => {
    //采用fetch发起请求
    const req = fetch('http://127.0.0.1:4000/api/getUserInfo', {
      method: 'get',
      headers: {
        'Content-Type': 'application/x-www-form-
          urlencoded'
      }
    })
    req.then(stream =>
      stream.text()
    ).then(res => {
      document.getElementById('data').innerText = res;
    })
  }
</script>
</html>

功能就是点击“获取用户信息”按钮,调用端口号为4000的服务接口。下面看一下端口号为4000的服务端代码。

const Koa = require('koa')
const cors = require('@koa/cors');
const app = new Koa()

const Router = require('koa-router')

const router = new Router()

router.get('/api/getUserInfo', async ( ctx ) => {
  ctx.body = 'liujianghong'
})

app.use(router.routes())

app.listen(4000, () => {
  console.log('server is running, port is 4000')
})

上述代码实现的功能比较简单,就是调用/api/getUserInfo接口时,返回一个字符串。下面看一下端口号为3000的服务端页面,调用端口号为4000的服务接口,结果会不会正常返回,效果如图2-18所示。

0

图2-18 跨域调用效果图

浏览器报错了,表示这个资源获取是跨域的。这就是浏览器出于安全考虑,做出的同源策略。在平时的开发场景中,有时候是需要跨越调用接口的,应该如何做呢?

Koa解决同源策略的实质是校验请求头,这里有一个协商的过程,第一次请求过来,会问一下服务端:“你好!我是跨域请求你这边的资源,你同不同意?”只有服务端同意后,才可以跨域请求。Koa官方提供了一个中间件@koa/cors用于解决这个问题,代码如下。

const Koa = require('koa')
const cors = require('@koa/cors');
const app = new Koa()
const Router = require('koa-router')

const router = new Router()

router.get('/api/getUserInfo', async ( ctx ) => {
  ctx.body = 'liujianghong'
})

// 加载cors中间件
app.use(cors({
  origin: '*'
}));

app.use(router.routes())

app.listen(4000, () => {
  console.log('server is running, port is 4000')
})

这里只增加了@koa/cors中间件,并且通过App装载就可以了。origin设置为“*”,代表任何URL都可以进行跨域请求。再次运行程序,发现跨域的请求可以正常访问后端数据了,效果如图2-19所示。

0

图2-19 设置cors后的跨域请求

这次可以看到请求正常返回,控制台没有报错。@koa/cors中间件还可以设置很多参数,比如允许哪些方法进行跨域请求,具体用法参考官方文档https://github.com/koajs/cors

注意

装载@koa/cors中间件一定要在koa-router之前,如果在请求过程中还没有进行cors设置,跨域问题会依然存在。