API 调用是开发者在小程序开发过程中经常会遇到的问题,本期我们以为调用豆瓣电影 API 为例具体来看 API 的调用过程以及常见的一些问题。
测试用到的小程序是「电影周周看」,内容来自清华大学软件学院刘强副教授在「学堂在线」上的小程序开发系列课程
简单了解豆瓣 API
为了简化课程讲解,我们将采取直接调用的方式,没有使用 OAuth2 的认证,也没有申请和使用对应的 apikey,所以这种调用会有比较严格的受限。
我们会以调用豆瓣电影条目信息 API 为例,这个 API 可以用来获取电影详情信息,具体怎么调用呢?
先来看下它的
URI:/v2/movie/subject/:id,
在这个目录下有一个冒号 id,这是一个 Restful 的 API 设计方式,就是将你要指定的目标电影的 id 直接在 URI 的路径中进行包含,而不是通过 querystring 的方式来指定。
我们通过浏览器来试一下这个 API 调用的结果。先输入豆瓣 API 的域名,再连接上 API 的路径,最后加上目标电影的 id,这里我们指定的是电影「教父」的 id 值 1291841 。
通过这样的一个 http request 请求返回的原始数据是一个 json 对象的格式文本,在它的 json 对象的格式中返回来很多对应的字段信息,比如 rating 反映了这部电影的各种评分,wish count 字段反映了有多少人想看这部电影,images 字段给出了三个不同尺寸的电影海报图片 URL,title 字段给出了电影的标题等等。
403 Forbidden 问题的干扰
在前面的逻辑中,每一次 detail 页打开,在它初始化的时候就能够从 options 参数对象中获取到本次要打开要展示的目标电影的 id,接下来就需要调用豆瓣的这个条目信息 API。将这个 id 传过来,然后由这个 API 返回 id 对应的电影详情数据。
前面我们看到过完整的 URL 是这个样子。
目标电影的 id 是直接 通过 URL 路径的一部分来直接进行传递的。所以这个地方的 data 属性我们不用定义,因为没捎带额外的数据,header 属性我们也不定义,method 参数默认用 GET 方法,因此也不指定。
那这里就指定了一个 success 的回调函数,我们先看一下它接收到的 response 对象会是什么,执行一下调用,返回的却是 403 Forbidden。
从返回的结果看,里面对应的「教父」电影的 id 值是 99 ,前面我们了解到「教父」电影的 id 值是 1291841, 替换正确的 id 后我们再调用一次。
这个时候我们得到了正确的 URL,它和我们刚才通过浏览器直接访问条目信息 API 时是一样的 URL。
通过浏览器直接访问能正确地获得对应的 json 对象文本,通过小程序 request 的方法调用API 返回的却是 403 Forbidden,这是什么原因?
这是因为, 在今年年初,来自小程序的直接、公开的调用过多,豆瓣 APIserver 就将来自于小程序的直接调用给禁止了。
大家可能会问,那么豆瓣的 APIserver 怎么样判定对它的调用请求是来自于小程序呢?
小程序在它的每一个请求中都设置了固定的 Referer 的 header 字段 ,豆瓣的 APIserver 收到一个请求后,如果它发现这个 Referer header 的取值是小程序框架固定的格式,它就会直接返回一个 403 Forbidden 的 response。
那我们是否可以在 request 调用的时候通过定义 header 参数来修改对应的 Referer header 的取值?小程序框架它同时也将对 Referer header 的设置给禁掉了,也就是说我们在 request 调用时,在 header 参数中来设置 Referer 的 header 也是无效的。
从这一点我们也可以看到,小程序对它的每一个 http 请求打上了自己的一个固定烙印,那么我们要怎么解决这个问题?
我们可以通过一个中间的服务器来对 Referer 的 header 进行重新设置,具体的,我们可以 在小程序和豆瓣 API server 之间设置一个对应的 nginx 转发代理来实现。
我们在 nginx 转发代理中做一个page,将它接收到的 Referer 的 header 改成一个随便的值。nginx 转发代理收到小程序发送过来的请求后,会将小程序中固定住的 Referer header 烙印给抹掉,然后再将 request 发送豆瓣 APIserver ,豆瓣 APIserver 会将这个请求当作普通的请求进行处理,这样一来就能正常地返回对应的详情数据。
因为小程序请求的每一服务器域名必须先进行 ICP 备案,然后才能在后台等级配置使用,在课程讲解的时候我们还来不及对自己的域名进行备案,所以我们就直接使用一个第三方的针对豆瓣 API 的转发代理。
我们将这个请求从原来的直接发送给都豆瓣 APIsever 改为先发给 nginx 转发代理。
运行之后,我们发现这次收到的是一个 400 bad request 的响应。这个问题又如何解决?
针对这个问题我们必须在请求的时候对 http 请求设置一个特定的 content type 的 header 的取值。
当我们将这个取值设置为 json 之后,本次 request 方法调用没有抛出任何错误,而且在 success 回调函数处理时显示的 是一个 200 OK 的 response。
并且在 data 属性中,我们成功地保存了本次 API 调用返回的目标电影详情数据对应的 json 文本对象转化而来的一个 javascript 对象,而它实际上就是对这个目标电影各个详情数据的一个封装。
在收到返回到目标电影详情数据之后,我们就可以将它保存到页面的一个内部状态数据中。
我们先通过 onload 方法的 this 指针将这个页面的页面对象的引用做一个保存,将其保存到一个 that 变量中,然后在 success 回调函数处理的时候直接通过 that 变量来获取外部页面对象的引用,接着,再访问其中的 setData 方法。
调用之后,我们在 APPdata type 中进行查看,最终在 detail 页面中找到了名为 movie 的内部状态变量,这其中就包含了这部电影对应的各种详情数据。
detail 页的具体展示
接下来我们就将接收到的详情数据渲染输出到视距图中进行展示。具体的展示办法就是通过新增的内部状态变量 movie 对电影详情对象中的相关属性进行数据绑定来实现。
我们将接收到的电影详情对象中的 summary 属性、 wish count 属性、collect count 属性等等都通过数据绑定的方式渲染输出到对应的元素上进行显示。
对 images 元素我们也可以将它的高度、宽度做一个设置,做一个简单的样式优化。
这时,我们再看看页面中的其他的两部电影,点击它们以后返回的是 404 Not Found。这是因为这两部电影的 id 并不是真实的豆瓣 id,因此豆瓣电影的 APIserver 就返回了一个 404 响应。
实际上这时候我们还是进入了 success 回调函数的处理,我们依然将接收到的 data 属性的取值保存到了内部状态变量 movie 中,所以这个时候 movie 的取值被保存成了这样的一个对象,但它不是一个正常的电影详情数据的对象,在这个对象中找不到我们需要的各种字段数据。
因此在一般处理中,我们需要单独做一个判断,只将返回的正常数据保存到内部状态变量中。我们再来判断一下它是不是一个 200 OK 的 response。
这个时候我们并没有在 detail 页上来新增这么一个内部状态变量 movie,而只有当这个电影能真正找到,返回的是一个 200 OK 的 response 的时候,对应的 movie 这个内部状态变量才有可能添加。
我们将其他两部电影的 id 都改为正确的值,之后再点击每一部电影,它对豆瓣 APIserver 的调用都能正常运行。
以上就是本期关于豆瓣电影 API 调用的全部内容。
来源:高校微信小程序开发大赛