Restful Api 总结
# RESTful API开发 ### 什么是REST? - 简介 ``` REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。 RESTful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。 RESTful API,就是符合REST风格开发出来的应用程序接口。 ``` - 资源 ``` 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 | - 数据 通常Restful API 风格的数据都采用JSON格式传输。 - 测试工具 - 说明:postman是一款非常好用的测试工具,能够轻松模拟各种请求 - 安装:下载完安装包,一路next即可。 ### 原生实现 - 准备数据(放在内存) ```python posts = [ { 'id': 1, 'title': 'Python基础', 'content': '人生苦短,我用python' }, { 'id': 2, 'title': 'HTML', 'content': '几个标签的故事' } ] ``` - 获取资源列表 ```python @app.route('/posts/') def get_posts_list(): return jsonify({'posts': posts}) ``` - 获取指定资源 ```python @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]}) ``` - 添加新的资源 ```python @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 ``` - 修改指定资源 ```python @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]}) ``` - 删除指定资源 ```python @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': '数据已删除'}) ``` - 错误页面定制 ```python # 定制400错误显示 @app.errorhandler(400) def bad_request(e): return jsonify({'error': 'bad request'}), 400 # 定制404错误显示 @app.errorhandler(404) def page_not_found(e): return jsonify({'error': 'page not found'}), 404 ``` ### flask-httpauth - 说明:专门进行身份认证的扩展库,使用非常方便。 - 安装:`pip install flask-httpauth` - 使用: ```python # 导入类库 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 ``` - 保护指定路由 ```python @app.route('/posts/<int:pid>', methods=['DELETE']) # 需要认证才能访问 @auth.login_required 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': '数据已删除'}) ``` ### flask-restful - 说明:是一个快速实现restful api开发的扩展库,使用比较方便。 - 安装:`pip install flask-restful` - 使用: ```python from flask_restful import Api, Resource # 创建资源管理对象 api = Api() class UserAPI(Resource): # 保护类中所有的函数 decorators = [auth.login_required] def get(self, uid): return {'User': '获取'} def put(self, uid): return {'User': '修改'} # 可以单独保护 # @auth.login_required def delete(self, uid): return {'User': '删除'} class UserListAPI(Resource): def get(self): return {'UserList': '获取列表'} def post(self): return {'UserList': '添加资源'} # 添加资源 api.add_resource(UserAPI, '/users/<int:uid>') # 可以添加多个路由 api.add_resource(UserListAPI, '/users/', '/u/') # 若创建与初始化分开, 那么一定要将初始化放在添加资源之后 api.init_app(app) ``` ### 基于token的身份认证 - 说明:移动APP的身份标识,使用cookie比较麻烦,使用用户名和密码有风险,使用token比较合适。 - 使用: ```python @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.encode('utf8')) g.username = data['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) token = s.dumps({'username': g.username}) return jsonify({'token': token.decode('utf8'), 'expires': 3600}) ```