RESTFul API
什么是RESTFul?
-
简介
REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
RESTFul是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。遵循restful风格开发出来的应用程序接口,就叫RESTFul API。
RESTFul的接口都是围绕资源以及对资源的各种操作展开。
-
资源
所谓的资源就是在网络上存在的任意实体,哪怕是一条消息。
-
操作
所谓的操作就是对资源的CURD。在开发者设计良好的前提下,对网络资源的任意的动作都可抽象为对资源的CURD。RESTFul对网络资源的操作抽象为HTTP的GET、POST、PUT、DELETE等请求方法以完成对特定资源的增删改查。具体对照如下:
方法 行为 例子 GET 获取所有资源 http://127.0.0.1:5000/source GET 获取指定资源 http://127.0.0.1:5000/source/250 POST 创建新的资源 http://127.0.0.1:5000/source PUT 更新指定资源 http://127.0.0.1:5000/source/250 DELETE 删除指定资源 http://127.0.0.1:5000/source/250 -
数据
通常传输的数据都采用JSON格式,有时也会使用GET传参
-
工具
说明:postman是一款非常好用的API开发测试工具,可以非常方便的模拟各种请求
提示:下载安装包,一路NEXT完成安装,网址:https://www.getpostman.com
原生实现
-
准备数据
# 测试数据
posts = [
{
'id': 1,
'title': 'Python语法',
'content': '别人都说python语法很简单,但是每次问题都出在语法上'
},
{
'id': 2,
'title': 'HTML',
'content': '不就是几个标签的问题嘛,但是最好细心点'
}
]
-
获取所有资源
# 获取资源列表
@app.route('/posts')
def get_posts_list():
return jsonify({'posts': posts})
-
获取指定资源
# 获取指定资源
@app.route('/posts/<int:pid>')
def get_posts(pid):
p = list(filter(lambda p: p['id'] == pid, posts))
if len(p) == 0:
abort(404)
return jsonify({'posts': p[0]})
-
添加新的资源
# 添加新的资源
@app.route('/posts', methods=['POST'])
def create_posts():
if not request.json or 'title' not in request.json or 'content' not in request.json:
abort(400)
# 创建新资源
p = {
'id': posts[-1]['id'] + 1,
'title': request.json['title'],
'content': request.json['content']
}
# 保存资源
posts.append(p)
return jsonify({'posts': p}), 201
-
更新指定的资源
# 修改指定资源
@app.route('/posts/<int:pid>', methods=['PUT'])
def update_posts(pid):
p = list(filter(lambda p: p['id'] == pid, posts))
if len(p) == 0:
abort(404)
if 'title' in request.json:
p[0]['title'] = request.json['title']
if 'content' in request.json:
p[0]['content'] = request.json['content']
return jsonify({'posts': p[0]})
-
删除指定资源
# 删除指定资源
@app.route('/posts/<int:pid>', methods=['DELETE'])
def delete_posts(pid):
p = list(filter(lambda p: p['id'] == pid, posts))
if len(p) == 0:
abort(404)
posts.remove(p[0])
return jsonify({'result': '数据已删除'})
-
错误定制
@app.errorhandler(404)
def page_not_found(e):
return jsonify({'error': 'page not found'}), 404
@app.errorhandler(400)
def bad_request(e):
return jsonify({'error': 'bad request'}), 400
-
flask-httpauth身份认证
# 导入类库
from flask_httpauth import HTTPBasicAuth
# 创建对象
auth = HTTPBasicAuth()
# 认证的回调函数
@auth.verify_password
def verify_password(username, password):
# 此处的认证实际上应该查询数据库
if username == 'Jerry' and password == '123456':
return True
return False
# 认证错误定制
@auth.error_handler
def unauthorized():
return jsonify({'error': 'Unauthorized Access'}), 403
保护指定的路由:
# 获取资源列表
@app.route('/posts')
# 保护路由(需要认证才能访问)
@auth.login_required
def get_posts_list():
return jsonify({'posts': posts})
flask-restful
-
说明:是一个实现RESTFul API开发的扩展库
-
安装:
pip install flask-restful
-
使用:
from flask_restful import Api, Resource
api = Api(app)
# 创建资源类,通常一个完整的资源需要两个资源类
class UserAPI(Resource):
def get(self, uid):
return {'User': 'GET'}
def put(self, uid):
return {'User': 'PUT'}
def delete(self, uid):
return {'User': 'DELETE'}
class UserListAPI(Resource):
def get(self):
return {'UserList': 'GET'}
def post(self):
return {'UserList': 'POST'}
添加资源,可以一个资源指定多个路由地址
api.add_resource(UserAPI, '/users/<int:uid>', '/u/<int:uid>') api.add_resource(UserListAPI, '/users')
若创建Api对象时没有指定app,那么指定app的位置应放在添加资源之后
api.init_app(app)
4. 添加认证
```python
class UserAPI(Resource):
# 添加认证
decorators = [auth.login_required]
-
基于token的认证
# 认证的回调函数
@auth.verify_password
def verify_password(username_or_token, password):
# 此处的认证实际上应该查询数据库
if username_or_token == 'Jerry' and password == '123456':
g.username = username_or_token
return True
# 再次尝试是否是token
s = Serializer(app.config['SECRET_KEY'])
try:
data = s.loads(username_or_token)
g.username = data.get('username')
return True
except:
return False
# 获取token
@app.route('/get_token')
@auth.login_required
def generate_token():
s = Serializer(app.config['SECRET_KEY'], expires_in=3600)
return s.dumps({'username': g.username})
-