使用Flask创建REST式WEB服务十分简单。使用熟悉的route()装饰器及其methods可选参数可以声明服务及其所提供资源URL的路由。处理JSON数据同样简单,请求中的JSON数据可以使用request.get_json()转换成字典格式,还可以使用Flask提供的辅助函数jsonify()从Python字典中生成需要包含JSON的响应。
一. 创建API蓝本
REST式API相关路由是应用中一个自成一体的子集,为了更好地组织代码,我们将这些路由放到独立的蓝本中。API蓝本的基本结构如下:
如果以后需要创建一个向前兼容的API版本,可以再添加一个带版本号的包,让应用同时支持两个版本的API。api蓝本在api包构造文件中定义:
from flask import Blueprint
api = Blueprint('api', __name__)
from . import authentication, comments, errors, posts, users
注意:一定要导入蓝本中的所有模块,这样才能注册路由和错误处理程序。因为这些模块同时也要导入蓝本对象以定义路由,所以这些模块都在__init__.py的底部导入,避免出现循环导入。
app/__init__.py:注册API蓝本
...
def create_app(config_name):
app = Flask(__name__)
# 使用对象始化app.config
app.config.from_object(config[config_name])
config[config_name].init_app(app=app)
...
# 注册API蓝本
from .api import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/api/v1')
return app
注册api蓝本时指定了一个URL前缀,因此蓝本所有URL都以/api/v1开头。使用域名部署时,我们也会通过subdomain参数指定子域。(例如https://developer.github.com/是https://github.com/的一个子域)。
二. 错误处理
REST式WEB服务将请求的状态告知客户端时,会在响应中发送适当的HTTP状态码,并将额外信息放入响应主体。API返回的常见HTTP状态码如下:
HTTP状态码 | 名称 | 说明 |
200 | OK | 请求成功 |
201 | Created | 创建了一个新资源 |
202 | Accepted | 请求已接收,但仍在处理中,将异步处理 |
204 | No Content | 请求已成功处理,但返回的响应没有数据 |
400 | Bad Request | 请求无效或不一致 |
401 | Unauthorized | 请求未包含身份验证信息,或者提供的凭据无效 |
403 | Forbidden | 禁止访问 |
404 | Not Found | 资源未找到 |
405 | Method Not Allowed | 指定资源不支持请求使用的方法 |
500 | Internal Server Error | 服务器发生错误 |
404,405,500错误由Flask自己生成,而且一般会返回HTML响应。这会使客户端很困惑,因为客户端期望所有的响应都是JSON格式。解决办法是“内容协商”。根据HTTP的内容协商机制,在错误处理程序中根据客户端请求格式来改写响应。
app/main/errors.py:使用HTTP内容协商机制处理错误:
from flask import render_template, request, jsonify
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html:
response = jsonify({'error': 'not found'})
response.status_code = 404
return response
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html:
response = jsonify({'error': 'server error'})
response.status_code = 500
return response
return render_template('500.html'), 500
# 405通常只发生在API中,我们直接返回json响应
@main.app_errorhandler(405)
def method_not_allowed(e):
response = jsonify(error="The method is not allowed for the requested URL.")
response.status_code = 405
return response
新版的全局错误处理程序检查Accept请求首部(解码为request.accept_mimetypes),根据首部的值决定客户端期望接收的响应格式。仅当客户端接收的响应列表中包含JSON但不包含HTML时,才生成JSON响应。
其它状态码都由WEB服务生成,可在api蓝本的errors.py模块中以辅助函数的形式实现:
from flask import jsonify
from . import api
def bad_request(message):
response = jsonify({'error': 'bad request', 'message': message})
response.status_code = 400
return response
def unauthorized(message):
response = jsonify({'error': 'unauthorized', 'message': message})
response.status_code = 401
return response
def forbidden(message):
response = jsonify({'error': 'forbidden', 'message': message})
response.status_code = 403
return response