这个文档是2013年时写的一个api设计的考虑,内容可能已经不适合现在了,仅在这里做个备份

如何设计一个API服务

认证

api key

url中附加一个key

/api/posts/?key=aasd2323423asdfasdf

应用场景:

  • google map api
  • 简单的s -> s认证

缺点:

  • key容易泄漏
  • 不可靠,不适合做强认证

api签名

用双方都知道的密钥对api参数进行签名,可以防止参数伪造,如 http://shot.test.com/api/thumb/?blur=0&client=test&sign=0881307c9f1e440f05e4cc58075c9485&size=L&url=http%3A%2F%2Fwww.douban.com

应用场景:

  • 网银交易的第三方接口常用
  • 资源地址加密,如图片、视频,可以防止直接抓取
  • b -> s, s -> s均可

Basic auth

api利用basic auth来做认证,可以用basic auth用户名来取代api key

curl https://api.stripe.com/v1/charges \
	-u sk_test_mkGsLqEW6SLnZa487HYfJVLf:
# or
curl https://sk_test_mkGsLqEW6SLnZa487HYfJVLf:@api.stripe.com/v1/charges

优点:

  • 方便调试,浏览器中访问认证一次,后续不需要再次输入
  • 调用方便,程序访问可以直接把认证信息写入url中
  • 用于s -> s

在tornado中实现basic auth:

<script src="https://gist.github.com/notsobad/5771635.js"></script>

数据结构

返回json数据结构,所有信息要在同一个wraper下。

  • 要返回dict而不是list (为什么?)
  • 要有操作时间字段 (为什么?)
  • 异常情况要给出正确的错误提示,也要返回正确的json结构

框架选择

推荐轻量级框架

  • web.py
  • tornado
  • flask
  • ….

推荐使用tornado

优点:

  • 高效, 部署时可以无需uwsgi
  • 异步,将一些db操作,第三方api访问放入异步,可以实现高并发

下面的代码实现了一个异步的api proxy

def getJSON(url, callback):
    http_client = tornado.httpclient.HTTPClient()
    response = http_client.fetch(url)
    cont = response.body
    http_client.close()

    ret = None
    try:
        ret = json.loads(cont)
    except:
        traceback.print_exc()
    return callback(ret)
  
class ProxyHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        url = 'http://www.xxx.com/abc.json'
        getJSON(url, callback=self.on_response)

    def on_response(self, ret):
        if not ret:
            raise tornado.web.httperror(500)
        self.write(ret)
        self.finish()

部署、缓存

api要有缓存,要根据api的具体情况定制缓存策略,下面给一种简单的缓存架构

在api server端,对于不同的接口,声明合适的http头,如Cache-control, expires等,缓存本身由中间层来做,如nginx、varnish等来负责。

last_modified = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time()))
web.header('Last-Modified',last_modified)
web.header('Cache-Control', 'max-age=86400')

如果你的应用使用nginx + uwsgi形式,可以使用uwsgi_cache

uwsgi_cache_path /home/data/cache levels=1:2 keys_zone=api:10m inactive=7d  max_size=600m;
server {
	listen 80;
	server_name xx-api;

	location / {
		uwsgi_cache api;
		uwsgi_cache_use_stale   error timeout invalid_header http_500;
		uwsgi_cache_key api$request_uri;

		include uwsgi_params;
		uwsgi_param UWSGI_CHDIR /var/www/api/;
		uwsgi_param UWSGI_SCRIPT wsgi_app;
		uwsgi_pass unix:////var/run/uwsgi_api/api.sock;
	}

}

类似的,如果你用nginx+proxy_pass的形式,也可以用proxy_cache模块

防滥用

nginx HttpLimitReqModule

参考链接: