这个文档是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
参考链接: