用 Tornado 试玩新浪微博开放平台 API

Tornado 作为一个 Python 的异步非阻塞服务器与轻量级 Web 框架, 相当令人着迷. 为了体验一把 OAuth2.0 认证, 我用 Tornado 搭建了一个网站, 调用新浪微博 API. 新浪微博的 OAuth2.0 认证的顺序如下:

  • 用户访问客户端, 客户端把用户带到新浪认证服务器去输用户名密码;
  • 新浪认证服务器认证完毕后, 将用户带到客户端某指定页面, 给这个页面传递一个 GET 参数 code;
  • 客户端某指定页面接收到 code 之后, 后台发起对新浪API服务器的POST请求, 用 code 换取 access_token;
  • 得到 access_token 后, 就可以用来调用各种需要用户登录之后才能调用的 API 了.

下面简单说明在 Python 3 下用 Tornado 如何完成上述四个步骤.

 

将用户带到新浪认证页面

假定我们要开发的客户端的域名是 dropthej.com , 在新浪新建一个应用, 在设置里填好这个安全域名, 这样才能跨站请求 XMLRequests.

这一步就是写 html 页面, 在 Tornado 中就是写模板. 假设我们在客户端中放置一个按钮叫做 “用新浪微博登陆”, 代码如下:

<form class="form" method="GET" action="https://api.weibo.com/oauth2/authorize">
     <input type="hidden" name="client_id" value="这里填你的App Key"/>
     <input type="hidden" name="redirect_uri" value="http://dropthej.com/auth_code"/>
     <button type="submit" class="btn btn-info form-control">Login</button>
</form>

这样只要一点这个按钮就跑到新浪的认证页面去了, 用户认证好了之后, 新浪会将用户带到你提供的 redirect_uri 地址, 这里就是

http://dropthej.com/auth_code?code=xxxxxxxxxxxxxxxxxxxxxx

所以我们要用 Tornado 来处理这个认证服务器发给我们的 code.

 

Tornado 后台用 code 去换取 access_token

这一部分有两步工作:

  • 得到 code
  • 用 code 换 access_token

配置一下 Tornado 的路由表, 让 r’/auth_code’ 路由到 AuthCodeHandler 这个 Handler. 这样我们重写这个 handler 的 get 方法就能获取到 code. 然后我们用 POST 方法把 code 发给新浪, 这里就能体现 Tornado 的异步非阻塞的强大了, Tornado 的 AsyncHTTPClient 能发起异步请求, 用 @gen.coroutine 装饰器可以把异步写得像同步的样子. 代码如下:

from urllib.parse import urlencode
import json
import tornado.web
from tornado import gen
from tornado.httpclient import AsyncHTTPClient
from handlers.token import TokenBaseHandler


class AuthCodeHandler(TokenBaseHandler):
    """
        已经获得用户授权, 向API服务器获取Token
    """
    @gen.coroutine
    def get(self):
        auth_code = self.get_argument("code", "No code")
        post_data = {
            "client_id": "你的App Key",
            "client_secret": "你的App Secret",
            "grant_type": "authorization_code",
            "code": auth_code,
            "redirect_uri": "http://dropthej.com"
        }
        http_client = AsyncHTTPClient()
        response = "init"
        body = urlencode(post_data)
        try:
            response = yield http_client.fetch(
                "https://api.weibo.com/oauth2/access_token",
                method="POST",
                body=body
            )
        except Exception as e:
            self.write(str(e))
            return;
        
        token_json = json.loads(str(response.body, encoding='utf-8'))

        # set token_json
        self.application.token_json = token_json
        self.write("<br/><br>" + str(token_json['access_token']))
        url = "http://dropthej.com/?tk=" + token_json['access_token']
        self.redirect(url)

由于我们仅仅用来玩一玩他的 API, 所以这个得到的 access_token 我们就不写到 cookies 里面去了, 而是采用放在 URL 里面的简单方法. 这样应用的每一个页面都带上 tk 参数, 就能保持用户的登录状态. 这里我们返回到了 http://dropthej.com/?tk=access_token 这个页面, 所以我们在 html 里面接收这个 tk 然后用来调用新浪 API 就可以了.

为什么都是发送 GET 和 POST 请求, 这个要用 Tornado 在后台来发送呢? 在前端用 javascript 来完成不行嘛? 因为这里要把 App Secret 发出去, 人家都叫 Secret 了你还传到前端去经过用户的电脑, 那是相当的不安全. Secret 就是设计来验证这是真正的客户端发出的请求, 所以要在后台发, 要有客户端而不是用户来完成.

 

使用 jQuery 来调用新浪 API 获取用户最新的首页微博

这不是重点就不多说了, 效果很好很让人开心, 主要代码如下:

var xyz;
$(function() {
  $.ajax({
    url: "https://api.weibo.com/2/statuses/home_timeline.json",
    type: "GET",
    dataType: "jsonp",
    data: {
      "access_token": ac_tk
    },
    success: function(result) {
      xyz = result.data.statuses;
      for (x in xyz) {
        $("#tbody").append("<tr><td>" + xyz[x].user.name + "</td><td>" + xyz[x].text + "</td></tr>");
      }
    }
  });
});

效果如图所示:

weiboapi