Web开发之旅–使用Flask 实现REST架构的API
原创: 程序猿与吉他狗 程序猿与吉他狗 -10-22
https://mp./s/dowlEqAWN9v9SD_Y2-eoKA
最近几年移动互联网的高速发展,业务逻辑被越来越多的迁移到客户端一侧,例如iOS和Android的各种App,服务器的主要功能(有时是唯一功能)是为客户端提供数据存取服务。在这种模式中,服务器变成了Web 服务或应用编程接口(Application Programming Interface,API)。Flask 是开发REST 架构Web 服务的理想框架,因为Flask 天生轻量。下面我们就来看看如何使用Flask实现API。
一、为什么要使用RESTful-API?
关于REST架构的相关很多细节可以参考书籍《RESTful Web Services Cookbook》,这本书详尽的介绍了实现REST架构的相关细节,REST的英文是representational state transfer(表现性状态转移),从字面理解就是将资源的呈现任务转移到前端,服务器后端只负责提供资源就行了,我们不需要再写模板,考虑前端呈现了,只需要把数据返回给前端,由前端自行处理就行了,REST中一个非常关键的特点就是无状态,例如我们不知道当前用户是否已经登录,不能使用上下文来实现用户状态的存储,每一个到来的请求服务器都认为是毫无关联的,这样也带来了更大的好处就是前后端完全解耦,对于弹性扩容提供了便利。RESTful-API还有一个好处就是区分版本,可以实现在不影响老版本用户的情况升级新的功能。使用Flask实现RESTful-API有很多种方式,既可以手动实现也可以使用Flask-restful扩展来实现,为了便于理解,本文主要通过手动实现,理解原理后使用扩展实现就很简单,看官方文档就可以搞定了。
二、资源定位及请求方法
前面讲到后端服务器实际只是为前端提供资源,那么后端的核心就是资源,什么是资源呢?例如前面实现的文章、用户等等存储在数据库中的内容,对数据库的操作有增删改查这四种方法,简称为CRUD,其实Http请求同样有方法与之对应,只是我们平时就用了GET和POST两种,那对应的方法分别是什么呢?
这里尤其要关注状态码,不要在资源不存在的时候返回200,而是应该返回404,用户权限不够的时候应该返回403等等,这个和我们前面的方法有很大区别,前面为了使用户体验更好,即使访问出错了,用户浏览器也能收到错误的信息,页面显示仍然正常。
REST架构的提出者Roy Fielding正是Http协议的主要设计者,他认为现在的网络服务存在很大的问题,明明我为你们设计了那么多的请求方法,结果你们删除资源都是用GET方法,完全背离了设计初衷,但是广大程序猿都是怎么好用怎么来,所以他在2000年的博士论文中提出了REST架构,我们来看看REST和我们之前使用的方法的资源定位的区别:
这样一对比以后是不是感觉瞬间清爽了,通过不同的请求方法来区分对资源不同的操作。
三、请求和响应的body
前面有两篇文章详细的讲了请求和响应,传统方法后端返回的是html文件,使用REST架构可选用JSON和XML,但是JSON和JavaScript 联系紧密,现在前端使用JavaScript实现的框架层出不穷,所以使用JSON来传递数据成为了首选方式,这里的响应内容和传统的有很大区别,因为请求是无状态的,例如分页查询的时候我们并不知道这个请求之前请求的是哪一页,所以在响应的时候要将下一页和上一页的URL返回。下面讲解具体如何实现RESTful-API。
四、具体实现
(一)创建蓝本和错误处理
前面我们创建过auth、blog、main这三个蓝本,这里的创建方法相同:
api_v1这里的v1是用来区分版本,如果后续功能发生变化,就可以新建一个v2的蓝本实现兼容。
app/api_v1/init.py:
app/init.py:在create_app工厂函数中注册
app/api_v1/errors.py:用于处理错误
app/main/errors.py:这个是之前用于处理错误的文件,但是加入api后会导致api接收的错误信息无法识别,所以也需要修改,思路就是识别请求头中的accpet_mimetypes,如果是json就返回json格式的响应。
(二)用户认证
用户认证因为REST架构请求无状态了,服务器无法记住客户端的任何信息,所以每次请求都必须携带认证信息,传统的认证方式服务器会把用户的认证信息存在会话中,不需要每次请求都携带认证信息,这里REST架构和传统方式的区别很大。
REST发送用户名密码不是通过POST表单的方式,而是将认证信息放在请求头的Authorization中,虽然我们可以自行实现,但是有已有的库对这些细节操作进行了封装,这里就使用flask-HTTPAuth来进行认证。
$pip install flask-httpauth
app/api_v1/authentication.py:
这个文件的内容稍微有点复杂,下面逐个进行解释:
先将HTTPBasicAuth类进行实例化,后面会用到它的3个装饰器,auth.verify_password:用来验证请求头中的认证信息是否正确,这里有两种认证方式,一是传统的账号密码,二是令牌方式。
auth.login_required:类似于Flask-login中的login_required来实现路由的保护,api.before_request这个装饰器是api这个蓝本的请求钩子,也就是请求这个蓝本下所有路由都需要先经过它来处理。
auth.error_handler:当认证发生错误时,由它来处理。
get_token这个视图函数是当用户认证成功后,可以通过它来获得令牌方便后续的访问,也就是后续请求只需要携带令牌就可以了,不需要每次都使用用户名和密码,因为令牌有过期时间,这样安全性更好一点,当然问题的关键还是要使用https来确保传递过程中不被攻击者轻易获取。
在verify_password中使用了User模型类的静态方法verigy_auth_token和实例方法generate_auth_token,这个是用来识别令牌是否有效以及这个令牌来自哪个用户的和为用户生成令牌。所以在models.py中的User类中需要添加这部分内容:
这样就实现了API的认证,访问权限的控制和传统方式一样,使用自行编写的装饰器来实现即可。
vv/app/api_v1/decorators.py:
在视图函数前使用这个装饰器就可以实现权限控制了。
(三)资源序列化及路由视图
flask-restful扩展库对这一块封装的很好,接下来我们手动实现资源的序列化,但是不推荐手动实现,这样会导致代码冗余和逻辑混乱,这里实现只是为了讲解原理。资源序列化简单的理解就是把资源包装成JSON数据返回给前端,如果手动实现的话,需要构造一个字典,把返回的内容以键值对的形式放入字典中,然后使用jsonify把字典转换成JSON格式。下面以posts这个资源为例:
vv/app/api_v1/posts.py:
通过id查询数据库,然后把查询结果使用to_json转换成字典,最后通过jsonify把字典返回,这个to_json是在模型中实现的,在models.py中的Post类添加该方法。
当查询全部post的时候由于数量较大,需要使用分页查询。
vv/app/api_v1/posts.py:
因为无状态的原因,这里需要将上一页和下一页的地址都返回给前端。
新建文章的方法:
vv/app/api_v1/posts.py:
这里使用了Post类的静态方法from_json来创建post实例,返回的时候把新建的结果返回给前端,且状态码为201,表示创建成功。下面实现models.py中Post类的from_json方法:
这里用到了自定义的异常,在app文件夹下新建一个exceptions.py:
最后就是修改文章的实现方法:
通过id查询到该文章,然后判断权限,修改post的属性,提交数据库,最后返回该文章。
本文简单的介绍了Flask实现RESTful-API的方法,设计一个良好的API还有很多的内容,这里就不再展开,因为它们与框架甚至是语言都无关的,有兴趣的朋友可以看看《RESTful Web Services Cookbook》,貌似全平台缺货,我有一个不太清晰的电子版,需要的可以留言。