公司项目的业务逻辑蛮复杂的,有时候我们需要测试 UI,就得让订单达到某一个特定的状态,往往需要在后台里先点个几分钟。其实最后我们还不是拿的后端返回的数据来填充 UI,所以为什么不直接把这个状态的数据构造好,直接拿来使用呢?所以我还是决定写个 Mock Server。

Mock Server 的功能很简单:

  1. 根据 API Endpoint,返回配置文件中指定的 JSON 文件;
  2. 可以局部匹配参数,返回指定的 JSON 文件。

后来 Android 端的小伙伴似乎也有点兴趣,但不想总是改代码指向 Mock Server,那行,我们就再加一个功能:3. 配置文件中没有指定的接口的请求,转发到开发服务器中。

前两部分很简单,就是写一个 API Server 而已。因为懒得碰其它语言,直接使用了 Swift + Vapor。每次收到请求,我们都读取一下配置文件,大概是这么个结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Pref: Decodable {
let endpoints: [Endpoint]
}

struct Endpoint: Decodable {
let endpoint: String
let filename: String?

let dispatch: [Dispatcher]?

struct Dispatcher: Decodable {
let parameters: JSONValue
let filename: String

func match(_ param: JSONValue) -> Bool {
return parameters.compare(param)
}
}
}

获得请求后,我们首先判断接口是否在 Pref.endpoints 中,如果有,则在看其参数是否存在一个子集处于 Endpoint.dispatch 中,如果有则使用其中的 filename.json,没有则 Fallback 到 Endpoint.filename.json。

对于其它的一切情况,返回 404。

第三点就比较麻烦了,一开始我想在 Mock Server 里再发一次请求,尽管咨询了后端的同学,但最终还是遇到了一些我暂时解决不了的问题:

  1. 多个请求同时来的时候会收不到一些响应;
  2. 用 Nginx 实现 HTTPS 的时候,响应会变成双倍。但 Vapor 3 直接实现 HTTPS,找遍了 Google 都没找到方法。

我倒腾了很久都没搞懂是什么情况……最后 Google 发现 Nginx 有一个东西叫做 proxy_next_upstream,我们可以指定多个 Upstream,如果一个有问题,就用下一个。所以我们可以写下这样的 Upstream:

1
2
3
4
upstream w {
server localhost:4343 max_fails=0 weight=200;
server real.dev.api.server.com:443 max_fails=0 weight=1;
}

值得注意的是要写下权重,避免 Nginx 轮询调用。

然后再写两个 Server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server { #A
listen 80;
listen 443 ssl;
server_name real.dev.api.server.com;

ssl_certificate servernew.crt;
ssl_certificate_key server.key;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

location / {
proxy_pass https://w;
proxy_set_header Host real.dev.api.server.com;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404 invalid_header non_idempotent;
}
}

server { #B
listen 4343 ssl;
server_name localhost;

ssl_certificate servernew.crt;
ssl_certificate_key server.key;

location / {
proxy_pass http://localhost:9090;
proxy_set_header Host real.dev.api.server.com;
}
}

如此一来,通过改 Host 或改代码指向本机 IP 的方式,我们就能通过 Server#A 先调用 Mock Server,如果配置文件中没有请求的接口,则返回 404,接着调用真实的开发环境接口。

Server#B 是因为 Server#A 的 proxy_pass 用的是 https,试了一下有问题,所以又做了一层。我并不清楚以上哪一行是多余的,反正能用(

现在我们就能无痛使用 Mock Server 获得假数据了。