Disqus API 科学使用指南

August 4, 2017   Disqus 博客相关

博客换用 Disqus 也有一段时间了,Disqus 的使用体验还是不错的,就是必须通过「科学上网」才能正常查看评论和发表评论。本来我是觉得无所谓,不过是翻过来的事,但是最近政策收紧,想要翻个身都有点难。为了解决这个问题,刚开始是打算自建一个评论系统,但是我的后端知识匮乏,去学的话还要花不少时间,而且真要写出来还是挺难的。然后想起来以前在 Jerry Qu 博客看到的 「Disqus 基础模式」,实质上是利用海外服务器做反向代理,前端解析 Disqus API 显示评论框,觉得还不错,就自己动手做了做。

标题里的「科学」和「科学上网」里的「科学」意思大概差不多,本文探讨的就是利用反向代理实现墙内用户访问在 Disqus 托管的评论并且发表新评论的方法。

基本原理

反向代理

用不专业的术语简单地说,国内用户访问不了 Disqus,而国外的主机可以访问,国内用户又可以访问这台国外主机,于是让国内用户向国外主机发出请求,国外主机连接 Disqus 并返回用户想要的数据,就是对 Disqus 的反向代理。直接代理整个评论框是不行的,但是 Disqus 官方给出了详尽的 API 调用手册,基本上覆盖了 Disqus 的所有功能,我们就可以利用一下。

目前加载 Disqus 评论数据需要连接到三个域名: disqus.com,disquscdn.com 和 disq.us,其中只有 disqus.com 无法被国内用户访问。(需要注意的是 disqus.com 只是被 DNS 污染,用 ip 直接访问还是可以的,因此用国内主机做中转应该是可以的。)

所以在国外主机上只需要反向代理一个域名就够了。在 VPS 上可以用 nginx 配置反向代理,示例:

server {

  #...原有代码

  location ~ ^/disqus/(.*) {
    proxy_pass https://disqus.com/;
    proxy_redirect off;
  }
}

这样当你远程访问 /disqus 的时候就相当于访问 disqus.com 了,把 API 里的接口地址替换一下就可以。除了 nginx,PHP 还可以试一下 cURL,支持在虚拟主机上部署,参考 fooleap/disqus-php-api

Disqus API

官方的 API 使用手册可以点击 这里 查看。基本上所有功能都有,但是有的功能需要登陆 Disqus 账号才能使用,因为我们的目的是让国内的朋友用上 Disqus,所以不必牵扯到登陆 Disqus。我们要实现的功能有评论计数(threads/details)、评论内容(threads/listPosts)和评论发表(posts/create),只需使用相应的三个接口就行了。

使用接口前需要获得一个 api_key,这个在文档里点「Applications」就可以申请一个,但这个 key 实际上是有限制的,不仅限制的使用频率,还不能提交评论。不过通过查看官方评论框的 post 数据,可以发现一个 E8Uh… 的 api_key。

E8Uh5l5fHZ6gD8U3KycjAIAk46f68Zw7C6eW8WSjZvCLXebZ7p0r1yrYDrLilk2F

用这个 api_key 是可以提交评论的,但是限制 RefererOrigin 都必须是 https://disqus.com。把 api_key 集成到 nginx 的配置里会是这样的:

server {

  #...原有代码

  location ~ ^/disqus/(.*) {
    proxy_pass https://disqus.com/api/3.0/$1?api_key=E8Uh5l5fHZ6gD8U3KycjAIAk46f68Zw7C6eW8WSjZvCLXebZ7p0r1yrYDrLilk2F&$args;
    proxy_set_header Referer https://disqus.com;
    proxy_set_header Origin https://disqus.com;
    proxy_redirect off;
  }
}

注意:因为 proxy_pass 里包含变量,所以要到 nginx.conf 里增加一条配置:

http{

  #...原有代码

  resolver 8.8.8.8;
}

这样只需把调用接口的地址替换成形如 /disqus/threads/listPosts.json 即可,并且不需要再提交 api_keyapi_secret 的值。

这又引发了另一个问题,因为限制了 Originhttps://disqus.com,当做出提交评论动作的服务器和做反向代理的服务器不是同一域名的时候,就会出现被阻止跨源访问的报错。要解决这个问题,一可以把两台服务器放到同一域名或者在同一台服务器上完成操作,二可以把提交评论的表单放到 iframe 或者弹出窗口里面。

最佳实践

有了以上原理,再好好研读全英文的 Disqus API,就能做出一个评论框了。下面详细说一下我做这些的思路。已经想到办法的建议不要往下看,我必须承认我的方法比较古老并且低效。我的代码都公开在 这里,可以直接用。

服务器配置

首先要 安装 好 nginx 然后做反向代理的配置。这里最好再 申请 一个 SSL 证书并且也配置好,通过 HTTPS 访问服务器效果会比较好。

后端比较熟练的可以直接在服务器上显示整个评论框,然后插到文章页就行。我选择的是前端获取内容再显示,因此后端只做了接受不同参数然后传给 Disqus 的功能。要在已安装 nginx 的服务器上安装 PHP,可以看这篇 教程

调用接口

现在假定服务器是 d.benwong.cn,通过上面的 nginx 设置后调用接口的地址变成形如 /disqus/threads/listPosts.json。大多数接口都需要传入 api_key,我的因为在服务器里配置了所以不用再单独写。调用接口后返回的数据格式有 JSON、JSONP,有的还可以是 RSS。为了避免跨源请求,我们要使用 JSONP。使用 JSONP 要传入 callback=xxxxxxxx 为接受数据的函数名,通过 jQuery 接收数据的话可以不用写。

下面说一下三个接口的用法:

threads/listPosts:使用 Disqus 的一般都会获得一个 shortname,像是 munen.disqus.comshortname 就是 munen,这个对应的是参数里的 forum。每篇文章都有一个 identifier,这个一般由博客程序生成,像我的是文章的 slugorder=asc 指定评论按时间顺序从早到晚排列,如果不指定的话就是按时间顺序从晚到早。用 GET 方式提交,综合起来就会是这样:

/threads/listPosts.json?thread:ident=[文章的identifier]&forum=[网站的shortname]&order=asc

返回的数据中,.response[X].id 是该条评论的 id,.response[X].thread 是文章的 id,这两个数据在提交评论的时候会用到。

返回的数据里如果 .cursor.hasNext 为真,就表示还有下一页,要加载下一页,就获取 .cursor.next 的值,赋给 cursor 接到上面的地址就能接收到下一页的数据,如:

/threads/listPosts.json?thread:ident=[文章的identifier]&forum=[网站的shortname]&order=asc&cursor=122134:12

threads/details:其实获取某篇文章评论总数有很多个接口,我是随便选的这一个。用 GET 方式提交 thread:ident=forum= 就行。返回的数据里的 .response.posts 就是评论数。

posts/create:用 author_name 指定评论者名字, author_email 指定评论者邮箱(非邮箱格式会报错),author_url 指定评论者网址(非网址格式会报错),message 指定评论内容(不可为空),thread 指定评论的文章 id,parent 指定评论回复的评论的 id(也就是父评论,不是回复评论的此值要保持 null)。邮箱和网址可以利用 inputtype="email"type="url" 限制。以上数据用 POST 提交,成功后返回评论信息。

前端渲染

我是用 jQuery 写的,其实用 vue 之类框架做会更高效一点。我的思路是先预留一个 div,然后把评论框 append 进去。每一条评论也 append 进去,并且指定每条评论的外层 div 的 id 为 comment-[id],对于子评论就可以直接 append 到 comment-[parent] 了。其实这个部分先把所有评论的 HTML 放到一个变量里,再一次 append,可能会比较好。

对于翻页的数据,我是直接循环加载进去,没有加分页按钮,觉得评论就是要全显示出来才好(其实是懒)。为了显示「X 小时前」,还引入了 moment.js,这里有个坑就是时区的问题,要把代码写成 moment(.createdAt + '+00:00').fromNow() 显示出来才正常。

关于判断是否科学上网的方法,我是利用原来 Disqus 引入的代码,改成这样:

function() {
  var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
  dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
  //以下是新增的代码
  dsq.onload = function()  {
     disqus_done = true;
  };
  //以上是新增的代码
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
}

然后加一个判断,2000 就表示如果 2 秒内加载不了 Disqus 的 embed.js 的话就判定为未连接科学上网:

setTimeout(function() {
  if (!disqus_done) {
    //在这里启动和谐版 Disqus

    $("#disqus_thread").hide();
  }
}, 2000);

评论的发布我用的弹出窗口,窗口里的内容本来想参考一下 Jerry Qu 大大的代码,没想到丢进去就能跑,太厉害了(所以就直接用了)。

还可以做的

Disqus API 里返回的数据没有邮箱,所以显示不了 Gravatar 头像,但是网站管理员登陆后获取一下 cookies 就可以获取 emailHash 了,参考 Authentication。具体怎么弄我还没尝试,但是 fooleap/disqus-php-api 已经做到了。

Disqus 对于所有访客评论都是需要管理员审核通过才会显示的,如果通过上面的方法获取到管理员权限的话就可以在前台显示待审核的评论。访客评论是收不到评论回复邮件的,可以自己做一个邮件服务器。

官方文档里也给出了用 Disqus 登陆的方法,登陆后就可以对评论评分,对文章点推荐等等,基本上可以做成套壳的 Disqus,实现完全的个性化。

参考资料

打赏支持

你可以点击 此处,扫描二维码给我打赏两块钱。简单几步,你就能让我在买煎饼时奢侈地多加一个鸡蛋:)