自从roy fielding博士在2000年他的博士论文中提出rest(representational state transfer)风格的软件架构模式后,rest就基本上迅速取代了复杂而笨重的soap,成为web api的标准了。
什么是web api呢?
如果我们想要获取一篇blog,输入http://localhost:9000/blog/123,就可以看到id为123的blog页面,但这个结果是html页面,它同时混合包含了blog的数据和blog的展示两个部分。对于用户来说,阅读起来没有问题,但是,如果机器读取,就很难从html中解析出blog的数据。
如果一个url返回的不是html,而是机器能直接解析的数据,这个url就可以看成是一个web api。比如,读取http://localhost:9000/api/blogs/123,如果能直接返回blog的数据,那么机器就可以直接读取。
rest就是一种设计api的模式。最常用的数据格式是json。由于json能直接被javascript读取,所以,以json格式编写的rest风格的api具有简单、易读、易用的特点。
编写api有什么好处呢?由于api就是把web app的功能全部封装了,所以,通过api操作数据,可以极大地把前端和后端的代码隔离,使得后端代码易于测试,前端代码编写更简单。
一个api也是一个url的处理函数,我们希望能直接通过一个@api来把函数变成json格式的rest api,这样,获取注册用户可以用一个api实现如下:
@api
@get('/api/users')
def api_get_users():
users = user.find_by('order by created_at desc')
# 把用户的口令隐藏掉:
for u in users:
u.password = '******'
return dict(users=users)
所以,@api这个decorator只要编写好了,就可以把任意的url处理函数变成api调用。
新建一个apis.py,编写@api负责把函数的返回结果序列化为json:
def api(func):
@functools.wraps(func)
def _wrapper(*args, **kw):
try:
r = json.dumps(func(*args, **kw))
except apierror, e:
r = json.dumps(dict(error=e.error, data=e.data, message=e.message))
except exception, e:
r = json.dumps(dict(error='internalerror', data=e.__class__.__name__, message=e.message))
ctx.response.content_type = 'application/json'
return r
return _wrapper
@api需要对error进行处理。我们定义一个apierror,这种error是指api调用时发生了逻辑错误(比如用户不存在),其他的error视为bug,返回的错误代码为internalerror。
客户端调用api时,必须通过错误代码来区分api调用是否成功。错误代码是用来告诉调用者出错的原因。很多api用一个整数表示错误码,这种方式很难维护错误码,客户端拿到错误码还需要查表得知错误信息。更好的方式是用字符串表示错误代码,不需要看文档也能猜到错误原因。
可以在浏览器直接测试api,例如,输入http://localhost:9000/api/users,就可以看到返回的json: