# wx.cloud.callContainer 技术原理和使用指南

无论是在微信小程序还是在公众号 H5 中，`cloud.callContainer` 是微信团队推荐的调用云托管服务的方式。

但云托管服务本身也提供公网访问链接，为什么不直接使用 `wx.request` 或者在网页中发起 XHR 请求呢？推荐引入 `callContainer` 这种方式有什么具体技术背景？

本篇给大家深度讲解 `callContainer` 调用

## 一、传统调用方式

我们先来看一下，`wx.request` 调用方式：

``` js
const result = await wx.request({
  url: 'https://www.example.com/postapi', //仅为示例，并非真实的接口地址
  method: 'GET' // 按照自己的业务开发，选择对应的方法
})

console.log(res);
```

`wx.request` 和 Web 浏览器中 `XHR` 都是一类，提供标准的网络请求方式，只不过 `wx.request` 是为微信小程序定制的。

这类请求大致分两个部分，`DNS` 和 `TCP连接`。

其中 DNS 是指将域名信息发送网络内 DNS 服务器，换取域名映射的服务器 IP 地址，为接下来连接做准备。

获取 IP 地址后，小程序就向此 IP 的服务器建立请求链接，按照请求方法发送数据包，然后等待服务器回包，完成整个请求。

在这里有两个变量，第一个是 DNS，如果用户网络中的 DNS 服务器被劫持，那么小程序收到的 IP 地址是错误的，而后向错误的 IP 地址发送请求包，这种情况被称为 `DNS劫持`

DNS 劫持的危害是非常大的，为了防止此情况，微信团队推出[移动解析 HttpDNS 服务](/miniprogram/dev/framework/ability/HTTPDNS.md)，避免 Local DNS 造成的域名劫持和跨网访问问题，**这是一项付费服务**。

DNS 之后，第二个变量就是连接，小程序发送的请求会直接经过公网，通过网络的各个节点到达你的服务器所在地。

比如小程序用户在广州，你的服务器在北京，那么这个请求包会一路经过网络沿途的各个运营商交换中心的「路由节点」转发，一直到达你的服务器，这个过程中沿途受干扰变数较多，而且距离越远，变数越大。

当然解决办法也是有的，在全国各地布置接入点，每个接入点和服务中心直接拉专线网络，这叫私有专线；或者将服务端搬到各个地域，组成多地域服务；不管用什么方法，还是需要资金来建设。

## 二、微信链路请求

在微信小程序中，`callContainer` 方式的代码是这样子的：

``` js
const res = await wx.cloud.callContainer({
  config: {
    env: '填入云环境ID', // 微信云托管的环境ID
  },
  path: '/postapi', // 填入业务自定义路径和参数，根目录，就是 / 
  method: 'GET', // 按照自己的业务开发，选择对应的方法
  header: {
    'X-WX-SERVICE': 'xxx', // xxx中填入服务名称（微信云托管 - 服务管理 - 服务列表 - 服务名称）
  }
});

console.log(res);
```

但实际来讲，以上 `callContainer` 最终也是经过 `wx.request` 请求发出去的，但是走的却并不是常规的道路。（在 Web 网页中是经过 XHR 发出的）

**先说结论：`callContainer` 本质仍然是 TCP 请求，只是路径是微信的专有路径**

在上一部分我们讲到 DNS 和公网的一些问题，在你使用 `callContainer` 发送请求的时候就已经全部解决了。

首先，DNS 解析，通过 `callContainer` 发送请求时，小程序压根就不请求 DNS 解析，而是直接向微信服务器固定发送请求包，这个包中涵盖如下信息：

- 云托管环境 ID（env）
- 云托管服务名称（service）
- 业务路径（path）
- 请求的数据（data）

这个请求包会直接经过就近节点进入微信遍布全国的专线网络，在就近地域内微信服务器中识别请求的内容，判断云托管环境 ID 在哪个地域，具体的地址是什么，然后向目标地址发送你的请求数据，并在这一过程中将用户的身份信息验证一下，带上用户的 openid 等其他身份信息。

当你云托管服务中收到请求时，header 中就可以看到 `x-wx` 开头的微信认证信息，至于业务数据，和正常通过 `wx.request` 收到的数据一模一样。

你的服务返回数据包，又会经过同样的路径返回，一直回到小程序中，第一次请求就结束了，这个时候除了服务器给的信息正常返回给你的前端业务层，还有一些信息在经过 `callContainer` 的时候被截留了。

这些截留的信息就是在第一次请求中，微信探查的「用户小程序」到「云托管服务」之间最佳的路径，以及加密的用户身份令牌。

之后发送请求的时候，`callContainer` 就会直接按照这个最佳路径发送请求包，而身份令牌可以在经过微信链路网关时被解析出来，最终以明文形式展示在服务 header 中。

而从 `callContainer` 经过 `wx.request` 发送请求，一直到收到回复，整个请求包都是经过端到端加密的，也就是说，即使用户的手机网络被抓包了，也是一堆加密信息，并没有什么可参考的价值。

![](https://res8.wxqcloud.qq.com.cn/wxdoc/e7c3987d-a61b-4123-9b7e-4faa243be9a9.png)

另外在整个微信链路中，可以有效过滤网络攻击，恶意请求，刷接口等行为，这些请求都会在进入链路时就被过滤到，最终到达云托管服务时，都是经过微信验证的正常请求。

如果你的业务本身并不需要用户的身份信息，则可以在 header 中添加如下记录：

``` bash
'X-WX-EXCLUDE-CREDENTIALS': 'unionid, cloudbase-access-token, openid'
```

以上是排除指令，如果不填写则会在链路通信中附带，填写则不附带。

因为附带信息会在占用一些时间（大概 10ms-50ms），所以根据自己的业务需要，合理选择附带信息。

例子如下：

``` js
const res = await wx.cloud.callContainer({
  config: {
    env: '填入云环境ID', // 微信云托管的环境ID
  },
  path: '/postapi', // 填入业务自定义路径和参数，根目录，就是 / 
  method: 'GET', // 按照自己的业务开发，选择对应的方法
  header: {
    'X-WX-SERVICE': 'xxx', // xxx中填入服务名称（微信云托管 - 服务管理 - 服务列表 - 服务名称）
    'X-WX-EXCLUDE-CREDENTIALS': 'unionid, cloudbase-access-token, openid' // 不附带用户unionid，openid，access—token
  }
});

console.log(res);
```

## 三、其他事项

![](https://res8.wxqcloud.qq.com.cn/wxdoc/7753f803-33bd-4690-8cf1-c2e754e8584c.png)

1）当你使用未登录模式时，本身就没有用户信息，建议添加 `X-WX-EXCLUDE-CREDENTIALS` 能起到一定的加速作用。
2）未登录模式虽然没有用户信息，但是安全性和链路效率仍然有效存在。
3）除了云托管之外，云开发 `callFunction` 请求云函数的原理也基本类似。
4）在成本方面，`callContainer` 的微信链路效果相当于 WAF 和 DDOS 高防叠加，在云托管服务中这些都是标准配置，无需你支付任何费用。
5）`callContainer` 不仅可以应用在微信云托管中，如果你自有的 CVM 服务器也需要这种链路服务优化，请在[官方交流群](https://www.cloudbase.vip/scan/?code=werun)联系我们，我们会根据你的业务情况提供最佳的解决方案。
