微信小程序的云函数是更好的方式?

虽然之前有文章吐槽过微信小程序的双标情况,但是从那时候也上手了一些小程序的开发,所以近期也接了一点私活来玩玩

这篇文章主要是想吐槽微信小程序开发过程中的两个问题,也顺便说了一下为什么会使用云函数

云函数 / FaaS (函数即服务) 是一种更加精简的服务提供模式,你只需要用鼠标开通一个语言的运行环境,上传一个代码,就可以根据不同的触发方式来运行你写的代码,可以用于补足微信小程序自身的补足

比如因为需要精确的控制代码的总量大小,很复杂的逻辑不可能在本地做

例如生成 PDF 或者干一些其他的骚活就必须要经过服务器,如果你没有服务器,FaaS 就是你需要的东西

通过微信调用云函数,云函数就可以为你生成出一个 PDF 存储到一个中心的文件存储池,然后再把地址返回来;反正用户是无法感知也没必要知道是本地还是远程生成了这个文件,反正目的是达到了

腾讯云开发的云函数其实并不是非常必要的东西,毕竟它也算是一项收费服务,只不过云开发在初期都会有一些免费额度,如果你的访问量不高,其实也还能接受

活是一个还算有保密性质的管理和查询工具,为了确保人员在外还可以通过公网方便的使用客户端来修改和查询一些东西

Humm… 管理系统用小程序暴露在微信小程序生态中我并不认为是一个妥当的想法,不过还是有办法保护的,对吧?

两个主要的问题让我选择了使用云函数

  1. 竟然不支持 HTTP PATCH Method

  2. 需要防止抓包

1. 不支持 HTTP PATCH Method

后端的服务设计的十分不错,GET POST PUT PATCH DELETE 各有各的职责,开发起来非常舒服

但是有个问题,微信小程序竟然不支持 PATCH 只能用 PUT

什么是 PUT 和 PATCH

PUT 用于新建或者更新资源,别忘了,PUT 的操作是幂等性的,请参阅 w3 的 RFC 2616 HTTP/1.1 标准文档

直接引用

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

引用文中讲述了 PUT 可以用于创建和修改 (整体覆盖型),不同于 POST 方法,如果你对一个地址执行多个 POST 方法则会建立多个完全不同的结果,使用 PUT 创建资源则会建立完全相同的结果,而且是唯一的。

如果 URI 指向的地址不存在资源,PUT 可以帮你建立一个资源;但是如果 URI 指向了一个存在的资源,它会使用新的数据整体覆盖到原有资源中

举个例子,我们现在对 http://example.com/books/1 发起了 PUT 请求,并且携带了 JSON 数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "The Children of the Nova",
"type": "Sci-fi",
"ratings": 80,
"authors": [
{
"name": "Clara Maxwell",
"gender": "male",
"age": 42
}
]
}

如果这个 /books/1 的资源还未存在,那么 PUT 方法应当为你创建出 /books/1 的资源

如果你想更新数据,例如这本书的 ratings 从 80 提高到了 85,当你再次用 PUT 方法的时候携带了以下数据

1
2
3
{
"ratings": 85
}

你傻眼了,本来有书名,类型,评分和作者的数据突然变成仅有评分的数据

对,这就是 PUT 的特性;如果你尝试使用 PUT 更新原有数据,请确保携带完全你要更新的数据,你不能单单对某个字段进行独立更新

这就是 PATCH 方法诞生的原因,它定义在 RFC 5789 标准当中

如果你在更新数据的时候使用 PATCH 方法,并且携带以下数据

1
2
3
{
"ratings": 85
}

之前的数据不会被重写,仅仅 ratings 被从 80 提高到了 85

简单来说,PUT 将会覆盖原有数据进行更新,PATCH 则是局部更新

拉跨的小程序

微信小程序只实现了 RFC 2616 标准中的 HTTP Methods,尚未提供 PATCH

但是!我在一个反馈中竟然找到了一些好玩的信息

参考这个问题 wx.request 缺少 PATCH 方法

下面有一个回答

各位,我发现了个彩蛋。

开发工具不支持 PATCH,但是真机调试是支持的,欧耶!!!

这就十分有趣了,明明微信开发者工具中不支持,甚至你使用 PATCH 方法还会报错,但是在手机上就支持了?

于是我也试了一下,是的,确实是直接支持了

那,电脑小程序呢?我试了一下

灵异的情况来了,它并没有按照我所想的方向走,事实上是,它并没有出错,也没有按照正常路线走

Network 选项卡中显示请求走了 PATCH 方法,但是服务端却按照 GET 方法传递回了信息,并没有更新服务端中的信息

这就很诡异了,我用本地的 NodeJS 简单的写了一个服务端,用来返回目前用户使用的 HTTP Method

果不其然,如果你在电脑小程序中使用微信小程序不兼容的 HTTP Methods (包括自定义 Methods) 都会直接被当作 GET 请求发出去

服务端接收的就是 GET 请求

看到这里我真的 JJYBB

微信小程序不知道是限制了还是做了枚举,导致开发者无法使用 Patch 方法,但是诡异的是,电脑上无论是开发者工具还是电脑小程序都无法按照正常路线走,但是手机上运行又无比正常

顺便说一下,官方从 2019 年就说后续会支持这个特性,今年是 2021 年了,所以微信的承诺基本上各位是可以忽略不计的

你要问我既然 PUT 也可以更新信息,为什么我一定要用 PATCH

前面提到,如果你要用 PUT 更新信息你必须要确保你具有完整信息,否则更新上去可能会把整个数据结构更崩

这个时候比如有两个页面,前一个页面获取到目前的数据,下一个页面才会更新信息

你如果要用 PUT,你就得把前一个页面的数据用参数的方式加到跳转页面时的路径,数据多的时候真的是噩梦,而且十分傻逼

或者在下个页面重新 GET 请求一次目前数据,然后再 PUT 请求,也很傻逼

或许你会问为什么不在同一个页面做两个页面的功能,利用 wx:if 的方法切换两个页面

这么维护真的挺操蛋的说实在的…

2. 防止抓包

第二个事情就是防止抓包,抓包,不得了

有些时候就不想被抓包,不想被第三方的客户端或者脚本利用

而且后端设计的 Token 系统还特别简单,建立一个具有相应权限的 Token 就可以访问数据或者修改数据了,也不会自动吊销 Token,除非后台管理吊销掉这个 Token

所以如果被抓包被第三方利用就 JJYBB 再继续 JJYBB

不过微信小程序连 PATCH 都不支持了,想都不用想 SSL Pinning 这种骚操作也是不可能做得了了

那怎么办,云函数吧

首先先来解决第一个不支持 PATCH 的拉跨问题

云函数支持比较好的就是 JavaScript,NodeJS

我比较喜欢用 axios 来发起各种请求,所以我就在 package.json 里面加上了 axios

还好微信还保留有一点人性,云函数可以在本地调试

如果不支持在本地调试,我不知道自己又要再骂微信多少次

把所有跟网络相关的请求都交给云函数,甚至还有一个奇妙用途,就是躲过微信小程序的域名必须备案的限制

哦对这个是可以躲过去的,代价就是你需要额外付一点访问费用

但是其他的好处当然是更多了,更加好用的网络请求库,更多的逻辑判断和实现

这都是在微信小程序本地无法实现的

对然后还有防止抓包

微信小程序跟云函数的沟通是采用的腾讯自己的通讯协议,所以普通的抓包就直接对云函数无效了

哦哦说到这点

微信小程序本身解包也非常容易,如果你把请求的 TOKEN 啊,地址啊和签名之类的敏感内容写在微信小程序里,这也是很危险的

所以这些东西也可以直接放到云函数里面请求,即使被解包对方也只能看到你调用了云函数,对方无法接触到云函数的代码

这也增加了多种方面的安全性

唯一一点就是云函数毕竟是 FaaS,如果有一段时间没人访问,例如下班时间,再次访问的时候需要唤醒 API,这时候需要一定的等待时间