最近基于 GitHub API 开发了一款图床 Chrome 插件 Picee, 现在已经开源并上架 Chrome 应用商店. 当中的过程涉及到一些有趣的知识点, 故将其记录下来.
GitHub 地址: https://github.com/jrainlau/picee
Chrome 商店下载地址: Picee
灵感
平时有写点东西的习惯, 但是奈何一直找不到合适的图床. 有人推荐以微博或者七牛来做图床, 但是总给我一种 "受制于人" 的感觉, 不知道什么时候就会被各种限制, 比如禁止图片外链等等. 后来发现其实 GitHub 是非常适合做图床的, 因为仓库里的文件都可以通过 https://raw.githubusercontent.com 这个链接直接外链以供下载. 但是如果为了写个文章而每次添加图片都需要一顿 Git 操作, 那么写作体验必定非常不好, 如果有更方便的办法就好了 -- 那就是 GitHub API.
GitHub API
GitHub 提供了一套完善的 API 以供操作, 几乎涵盖了开发一个完整 GitHub 客户端的所有功能. API 分为 REST 风格的 v3 版本和 GraphQL 风格的 v4 版本. 为了使用方便, 我选择的是 v3 版本. 具体的 API 细节可以在官方文档 https://developer.github.com/v3/ 查看.
要制作一个图床应用, 我们只需要用到上传文件的 API 即可. 但是在调用这个 API 之前, 首选需要用户对应用进行授权, 也就是所谓的登录操作.
授权
对于一般的 "查看" 操作, 是不需要授权的, 比如获取用户的公开信息, 获取公有仓库的 issues 等等. 但是有两个场景是需要授权的, 其一是任何对于仓库的 "增删改查" 操作(包括提交 issue, 评论等); 其二则是对于某 IP 有 API 的调用次数限制, 若这个 IP 调用 GitHub API 的次数过多则需要授权.
那么授权应该怎么做呢? 官方提供了三种办法, 分别是 Basic,OAuth2 token 和 OAuth2 key/secret.
Basic 授权
也就是最传统的账号密码授权方式, 我们可以在命令行用 curl 来测试之:
curl -u "账号: 密码" https://api.github.com
如果是正确的账号密码, 则会返回一系列的内容, 否则会返回错误信息.
对于开发来说, 我更推荐使用 Postman 来对 API 进行测试:
点开右边的 code , 可以看到 JS 代码:
其中 xhr.setRequestHeader("Authorization", "Basic xxxxxx"); 就是我们需要设置的授权 header, 当中的 xxxxxx 是这么来的:
btoa(username + ':' + password)
OAuth2 token 授权
对于账号密码来说, 轻易地在第三方平台输入其实并不那么安全, 那么有没有办法既能保障账户的安全, 又能实现授权的需求呢? 答案就是 OAuth token.
简单来说, OAuth token 相当于用户提供给第三方的一张授权令牌, 第三方通过这张令牌可以获得用户所允许使用的一系列权限, 但是却不会知晓用户的账号和密码, 于是便得以在有效保障用户账号安全的同时, 又能方便地对第三方应用进行授权.
在 GitHub 里, 可以在这个地方 https://github.com/settings/tokens 设置生成具有某些权限的 token:
最后在 Postman 里选择 OAuth 2.0 或者 Bearer Token, 然后把这串 token 粘贴进去即可.
其中的授权 header 为 Bearer token.
OAuth 2 key/secret 授权
这种授权方式是通过生成一对 key/secret, 来允许第三方获取用户的公开信息, 是一种只读的授权方式, 无法对仓库进行改写操作, 主要用于第三方登录, 故在这里不适用. 更多关于 key/secret 的内容可以查看阮一峰的《GitHub OAuth 第三方登录示例教程》, 写得非常生动详细.
在了解了三种授权方式之后, 我们就可以进行下一步操作, 实现图片的上传了.
图片上传 API
图片上传使用了 content API 的 接口, 通过 PUT 发送一条文件内容为 base64 的请求到指定的仓库目录.
这里着重圈出了必须把文件进行 base64 编码, 否则接口调用将会出错.
通过 btoa('hello world) 方法把 hello world 转成 base64, 然后放在 Postman 里测试一下:
看来效果是 OK 的, 接下来就是对图床插件进行开发的步骤了.
Chrome 插件开发
除了看官方文档 https://developers.chrome.com/extensions/devguide 学习插件开发以外, 也可以参考由 @小茗同学 所写的《[干货] Chrome 插件 (扩展) 开发全攻略》, 里面对于 Chrome 插件的开发有着详细的叙述, 非常值得一读.
读完上面推荐的文章之后, 我选择使用 vuejs 进行开发. 由于项目比较简单, 所以我没有用任何的打包工具, 直接通过 script 的方式引入 vueJS. 值得注意的是, Chrome 插件不允许行内 script 和行内 style, 所以任何的 CSS 和 JS 文件都必须通过本地文件链接的方式去使用. 另外由于我们的 JS 是运行在 Chrome 环境的, 所以可以放心大胆地使用 es 模块和 async/await 等高级语法, 而无需任何的构建工具参与.
但是在使用 VueJS 的第一步我就遇到了问题, 绑定了 new Vue() 的 DOM 元素竟然显示不出来. 经过查证, 原来 Chrome 插件有 Content Security Policy (CSP) 限制, 默认是不支持 eval(),new Function() 等方式运行代码的, 而完整版的 VueJS 恰好是这么干的(官网有说), 所以就出问题了. 那么怎么解决呢? 很简单, 在 manifest.JSON 里面声明一下就好了:
- // manifest.JSON
- {
- // ...
- "content_security_policy": "script-src'self''unsafe-eval'; object-src 'self'"
- }
这里我采用的是 popup 形式的插件, 弹出来的窗口就是项目所定义的 index.html. 如果要调试插件的页面, 可以直接在插件弹出的窗口点击右键, 然后点击 "检查", 就会弹出我们熟悉的开发者工具了. 如果插件文件有改动, 除了重新打开插件以外, 我们也可以在开发者工具通过 cmd + r 去直接刷新, 省去了多次点击的麻烦.
功能实现
经过前面的准备, 我们已经掌握了如何对 GitHub API 进行授权然后上传图片的办法, 接下来就是在业务逻辑里去实现它们. 我封装了一下原生的 fetch 方法, 让它更方便调用:
- const $fetch = (options) => {
- return Windows.fetch(options.url, {
- method: options.method || 'GET',
- headers: {
- "Content-Type": "application/json",
- "Authorization": localStorage.getItem('picee_token')
- },
- body: JSON.stringify(options.body) || null,
- mode: 'cors'
- })
- .then(async res => {
- if (res.status>= 200 && res.status <400) {
- return {
- status: res.status,
- data: await res.JSON()
- }
- } else {
- return {
- status: res.status,
- data: null
- }
- }
- })
- .catch(e => e)
- }
- export default $fetch
请求接口时携带的授权 header 所需的 token, 我把它们存放在插件的 localStorage 下, 方便调用.
有了请求接口的方法以后, 接下来就要完成选择图片和把图片转化成 base64 的工作. 这里我复用了另一个作品里的 chooseImg.JS 和 paste.JS 方法, 最终能够支持以选择, 粘贴, 拖拽的方式上传图片.
剩下的一些功能细节就不赘述了, 代码非常简单, 建议读者们自行查阅.
应用发布
准备好了 logo, 描述等善后工作之后, 就可以正式提交应用发布了. 我们可以在开发者信息中心里面把插件提交上去, 填入必要的信息以后点击发布, 等待审核完成. 但是在此之前, 你必须支付 5 美元的开发者注册费, 国内的开发者在完成这一步的时候可能会遇到蛮大的困难, 这一个问题在知乎也有讨论: 如何在中国使用信用卡支付 "Chrome 网上应用店" 开发者注册费? https://www.zhihu.com/question/54438738 . 我是通过万能的淘宝搞定的.
在完成注册之后和发布以后, 就能看到插件的主页了:
值得注意的是, 刚发布的插件是暂时不能被搜索出来的, 需要等待一段时间以后才能搜索出来.
至此, 整个插件的开发 -- 发布流程就已经完成了.
来源: https://segmentfault.com/a/1190000019116373