组团学

RESTful

阅读 (167563)

一、RESTful Api设计风格

1、什么是RESTFul

  • 简介

    REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。

    RESTFul是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。遵循restful风格开发出来的应用程序接口,就叫RESTFul API。

    RESTFul的接口都是围绕资源以及对资源的各种操作展开

  • 资源

    所谓的资源就是在网络上存在的任意实体,哪怕是一条消息。

  • 操作

    所谓的操作就是对资源的CURD。在开发者设计良好的前提下,对网络资源的任意的动作都可抽象为对资源的CURD。RESTFul对网络资源的操作抽象为HTTP的GET、POST、PUT、DELETE等请求方法以完成对特定资源的增删改查

2、协议

API与用户的通信协议总是使用https协议

3、域名

应尽量将API部署在专用域名下

https://www.xialigang.com

如果确定API很简单,不会有进一步扩展,可以放在主域名下

https://www.xialigang.com/api/

4、版本

应该将版本放入URL

https://www.xialigang.com/api/v1/

可以将版本放入http头信息中

5、路径

说明

  • 表示API的具体地址

注意

  • 每个网址代表一种资源,所以网址中不能有动词,只能是名词,而且所用的名词往往与数据库中的表名对应

示例

  • 错误示例

    https://127.0.0.1/api/v1/getStudents/
    
  • 正确示例

    https://127.0.0.1/api/v1/students/
    

6、使用正确的HTTP请求方式

方法 行为 例子
GET 获取所有资源 http://127.0.0.1:5000/api/source
GET 获取指定资源 http://127.0.0.1:5000/api/source/250
POST 创建新的资源 http://127.0.0.1:5000/api/source
PUT 更新指定资源 http://127.0.0.1:5000/api/source/250
DELETE 删除指定资源 http://127.0.0.1:5000/api/source/250
DELETE 删除所有资源 http://127.0.0.1:5000/api/source/

7、过滤信息

  • 概述
    如果资源较多,服务器不能将所有的数据一次全部返回给客户端,API提供参数,过滤返回结果

  • 参数

    • limit 获取多少个资源

      GET https://127.0.0.1:5000/api/v1/students/?limit=10

    • offset 偏移多少个资源
      GET https://127.0.0.1:5000/api/v1/students/?offset=10

    • page 要获取哪页的资源

    • per_page 每页有多个资源
      GET https://127.0.0.1:5000/api/v1/students/?page=1&per_page=5

    • sortby 根据哪个属性进行排序

    • orderby 排序的规则

      GET https://127.0.0.1:5000/api/v1/students/?sortby=age&orderby=desc

    • 类名小写_运算符_属性=值

      GET https://127.0.0.1:5000/api/v1/students/?student_gt_age=20

8、状态码

状态码 请求方式 说明
200 get OK 服务器成功返回资源
201 post、put、patch Created 用户新建或者修改资源成功
202 * Accepted 表示请求已经进入后台排队
204 delete No Content 用户删除资源成功
400 post、put、patch Bad Request 用户发出的请求有错误
401 * Unauthorized 用户没有权限(令牌、用户名、密码错误)
403 * Forbidden 表示用户得到授权(与401相对),但是访问是被禁止的
404 * Not Found 请求针对的是不存在的资源
405 * Method Not Allowed 用户请求的方式不被允许
406 get Not Acceptable用户请求的格式不可得(比如用户请求json格式,但是只有xml格式)
410 get Gone 用户请求的资源被永久删除,且不可在得到
422 post、put、patch Unprocessable Entity 创建对象时发生了验证错误
500 * Internal Server Error 服务器发生错误

9、错误处理

如果状态码是4xx,就应该向用户返回错误信息,一般返回内容中以error作为键,错误信息作为值返回

{ "error": "参数有误" }

10、链接相关的资源

  • 说明

    返回的结果中提供了链接,链向其他API方法啊,需要让用户不查看文档(项目文档)就知道下一步该干什么

  • 实例

    地址

    GET /students/<id>/

    { "name": "lucky", "age": 50, "link": "https://127.0.0.1/api/v1/test/" }

    restful风格

    { "name": "lucky", "age": 50, "link": { "rel": "collection 127.0.0.1:5000" "href": "127.0.0.1/api/v1/test/" "title": "测试界面" "type": "application/json" } }

    • rel 表示这个API与当前网址的关系
    • href 表示API的路径
    • title 表示API的标题
    • type 表示返回数据的类型

11、工具

  • 说明

    postman是一款非常好用的API开发测试工具,可以非常方便的模拟各种请求

  • 提示

    下载安装包,一路NEXT完成安装

    网址:https://www.postman.com/

二、原生实现RESTful

1、准备数据

# 测试数据 posts = [ { 'id': 1, 'title': 'Python语法', 'content': '别人都说python语法很简单,但是每次问题都出在语法上' }, { 'id': 2, 'title': 'HTML', 'content': '不就是几个标签的问题嘛,但是最好细心点' } ]

2、获取资源

  • 获取所有资源

    # 获取资源列表 @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]})

3、添加新的资源

# 添加新的资源 @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

4、更新指定的资源

# 修改指定资源 @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]}), 201

5、删除指定资源

# 删除指定资源 @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': '数据已删除'}), 204

6、删除所有资源

# 删除所有资源 @app.route('/posts', methods=['DELETE']) def delete_posts(pid): posts.clear() return jsonify({'result': '数据已删除'}), 204

7、错误定制

@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-restful

1、安装

pip install flask-restful

2、创建

from flask_restful import Api api = Api()
from .ext_api import api

3、加载

from exts import api api.init_app(app)

4、视图类

from flask_restful import Resource from myApp.models import User from flask import request # 创建用户处理类 class UserApi(Resource): def get(self, uid): u = User.query.get(uid) if u: return {'code':0, 'error':'','data':{'id':uid, 'uusername':u.uusername, 'uage': u.uage, 'usex': u.usex, 'uinfo': u.uinfo}} else: return {'code':1, 'error':'获取失败','data':{}}, 404 def put(self, uid): u = User.query.get(uid) if not u: return {'code':1, 'error':'获取失败','data':{}}, 404 json = request.json u.uusername = json.get('uusername') u.uage = json.get('uage') u.save() return {'code': 0, 'error': '', 'data': {'id': uid, 'uusername': u.uusername, 'uage': u.uage, 'usex': u.usex, 'uinfo': u.uinfo}}, 201 def delete(self, uid): u = User.query.get(uid) if not u: return {'code': 1, 'error': '获取失败', 'data': {}}, 404 u.delete() return {'code': 0, 'error': '', 'data': {}}, 204 class UserApiTwo(Resource): def get(self): u = User.query.all() userList = [] for user in u: userList.append({'id':user.id, 'uusername':user.uusername, 'uage': user.uage, 'usex': user.usex, 'uinfo': user.uinfo}) return {'code': 0, 'error': '', 'data': userList} def delete(self): uList = User.query.all() for u in uList: u.delete() return {'code':0, 'error': '', 'data':''}, 204 def post(self): json = request.json u = User() u.uusername = json.get('uusername') u.uage = json.get('uage') u.usex = bool(json.get('usex')) u.uinfo = json.get('uinfo') print(json) if u.save(): return {'code': 0, 'error': '', 'data': {'id': u.id, 'uusername': u.uusername, 'uage': u.uage, 'usex': u.usex,'uinfo': u.uinfo}}, 201 return {'code':1 ,'error':'创建失败', 'data':''}, 400

5、配置路由

# 添加资源,可以一个资源指定多个路由地址 api.add_resource(UserAPI, '/users/<int:uid>', '/u/<int:uid>') api.add_resource(UserListAPI, '/users/') # 若创建Api对象时没有指定app,那么指定app的位置应放在添加资源之后

6、拆分路由和视图

api目录下一个文件就是一个模型相关的视图类,urls.py仅做路由的匹配

from myApp.api.api_user import UserApi, UserListAPI # 配置路由 api.add_resource(UserAPI, '/users/<int:uid>', '/u/<int:uid>') api.add_resource(UserListAPI, '/users/')

7、添加认证

  • 说明

    Restful API不保存状态,无法依赖Cookie及Session来保存用户信息,自然也无法使用Flask-Login扩展来实现用户认证。所以这里,我们就要介绍另一个扩展,Flask-HTTPAuth

  • 安装

    pip install flask-httpauth

  • 示例

    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from flask_httpauth import HTTPBasicAuth # 创建对象 auth = HTTPBasicAuth() # 认证的回调函数 @auth.verify_password def verify_password(username_or_token, password): # 去数据库有中进行查找 u = User.query.filter(User.uusername == username_or_token, User.upassword == password).first() print(username_or_token, '=====>',password) if u: # if username_or_token == 'lucky' and password == '123456': g.username = username_or_token return True # 验证token s = Serializer(current_app.config['SECRET_KEY']) try: data = s.loads(username_or_token) g.username = data['username'] return True except: return False # 认证错误定制 @auth.error_handler def unauthorized(): return jsonify({'error': 'Unauthorized Access'}), 403

    获取token

    # 获取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})

    保护指定的视图类

    class UserAPI(Resource): # 添加认证 decorators = [auth.login_required]
需要 登录 才可以提问哦