安装 pip install flask-restful
手册参照 https://flask-restful.readthedocs.io/en/latest/
RESTful(表现层状态转化)
adds support for quickly building REST APIs
rest api是前后端分离的最佳实践,是开发的一套标准或规范,不是框架。
1、轻量,直接通过http,不需要额外的协议,通常有post/get/put/delete操作。http协议无状态,所有状态均保存在服务器端。
2、面向资源,请求即会返回数据链接URI。
3、数据描述简单,通过json(后端向前端返回数据)或者xml(ajax请求前端向后端)做数据通讯。
RESTful架构
(1)每一个URI代表一种资源,要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
(2)是客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词(GET获取资源,POST新建资源,PUT更新资源,DELETE删除资源,PATCH),对服务器端资源进行操作,实现"表现层状态转化"。
与之搭配使用的是Postman
模拟前端api客户端展示,不使用html页面,可能会有静态的交互,比如图片等。
下载:https://www.postman.com/downloads/
C:\Users\Administrator\AppData\Local\Postman\Postman.exe中,登录后进入初始界面
可以设置get或者post等请求 并相应添加请求头及请求体
REST前后端分离:
前端:app,小程序,pc页面
后端:没有页面,围绕mv实现:模型 视图 视图中使用api构建视图
# exts的init创建api对象
api = Api()
apps的init初始化api对象
api.init_app(app=app)
定义类视图:
from flask_restful import Resource
user_fields = {
'id': fields.Integer,
# 取数据库username的值 若无 默认为匿名 返回前端为name
'name': fields.String(attribute='username',default='匿名'),
'pwd': fields.String(attribute='password'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
class UserResource(Resource):
# 定义get请求的处理
@marshal_with(user_fields) # 也适用于列表 定制返回值格式
def get(self):
# 浏览器 访问 http://127.0.0.1:5000/user 出现msg "------>get" 因为浏览器是get请求
# 模拟post put delete操作需要进入postman
# return {'msg': '------>get'}
users = User.query.all()
# 对自定义的类无法直接序列化,使用marshal_with自定义返回数据格式
# userList = []
# for user in users:
# # userList.append(user) # Object of type User is not JSON serializabl
# userList.append(user.__dict__) # 对象有__dict__属性 Object of type InstanceState is not JSON serializable
# return userList
return users
# 定义post请求的处理
def post(self):
return {'msg': '------>post'}
# 定义put请求的处理
def put(self):
return {'msg': '------>put'}
# 定义delete请求的处理
def delete(self):
return {'msg': '------>delete'}
绑定,将类加入到api中,使用http://127.0.0.1:5000/user访问
api.add_resource(UserResource, '/user', endpoint='all_user')
flask url传参
# http://127.0.0.1:5000/user?id = 1
@app.route('/user/')
def user():
id = request.args.get('id')
# http://127.0.0.1:5000/user/1
@app.route('/user/<id>/')
def item(id):
return 'user{}详情'.format(id)
api class类url传参
# http://127.0.0.1:5000/user/1
# 定义传参测试类
class UserSimpleResource(Resource):
@marshal_with(user_fields) # 将user转成一个序列化的对象,
def get(self, id):
user = User.query.get(id)
return user
def put(self, id):
# 使用endpoint找路由
print('endpoint的使用:', url_for('all_user')) # endpoint的使用: /user
return {'msg': 'ok'}
def delete(self, id):
pass
api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user')
# put访问 http://127.0.0.1:5000/user/1
postman
Params中的参数添加是添加到url上的 Body中的参数添加是请求体的内容传输 纯文本添加选择x-www-form
有文件的选择form-data form-data的key栏中可以选择text/file
当add_argument中没有限制location=[‘form’]时,两种方式均可传值,但是设置后会只接受body中的传值
api class类请求参数传入post
# 创建RequestParser对象:
parser = reqparse.RequestParser(bundle_errors=True)
# 给解析器添加参数:
# 通过parser.add_argument('名字',type=类型,required=是否必须填写,help=错误的提示信息,location=表明获取的位置 form就是post表单提交)
# type的位置可以添加一些正则的验证。
# 例如:
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help='必须输入6~12位数字密码',location=['form'])
# 在请求的函数中获取数据:
# 可以在get,post,put等中获取数据,通过parser对象.parse_args()
args = parser.parse_args() # args是一个字典底层的结构,因此获取具体的数据时可以通过get
password = args.get('password')
结果返回前端
# 定义字典,字典的格式就是给客户端看的格式
user_fields = {
'id': fields.Integer,
# 取数据库username的值 若无 默认为匿名 返回前端为name
'name': fields.String(attribute='username',default='匿名'),
'pwd': fields.String(attribute='password'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
# 客户端能看到的是:id,name,pwd,udatetime这四个key
# 默认key的名字是跟model中的模型属性名一致,如果不想让前端看到命名,则可以修改结合attribute='模型的字段名'
# 自定义fields 继承Raw 重写方法format
class IsDelete(fields.Raw):
def format(self, value):
return '删除' if value else '未删除'
user_fields = {
'是否删除': IsDelete(attribute='isdelete'),
}
写一个程序,步骤如下:
# get请求http://127.0.0.1:5000/user返回userlist ----->再点击具体的一个获取详情
# 定义两个user_fields
# 1.用于获取用户的列表信息结构的fields:
user_fields_1 = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'uri': fields.Url('single_user', absolute=True) # 参数使用的就是endpoint的值 产生一个个的链接
}
# 2.获取具体用户信息展示的fields
user_fields = {
'id': fields.Integer,
'username': fields.String(default='匿名'),
'pwd': fields.String(attribute='password'),
'isDelete': fields.Boolean(attribute='isdelete'),
'isDelete1': IsDelete(attribute='isdelete'),
'udatetime': fields.DateTime(dt_format='rfc822')
}
# 此时id需要与fields中的数据name一致,不然无法拼接url
api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='single_user')
# return data 套叠结构 data必须是符合json格式
# 如果想要直接返回,则里面不能有自定义的对象,否则not JSON serializable
# 如果有这种对象,需要使用marchal()或 marchal_with()帮助进行转换。
# 1 marshal(friend_list,user_fields) # 用user_fields格式化friend_list后返回
# 2 marchal_with() 作为装饰器修饰请求方法
@marshal_with(user_friend_fields)
def get(self, id):
...
return data
# 例如:
user_friend_fields = {
'username': fields.String,
'nums': fields.Integer,
'friends': fields.List(fields.Nested(user_fields))
}
fields.Nested(fields.String) # ['str1','str2','str3']
fields.Nested(user_fields) # user_fields是一个字典结构,将里面的每一个对象转成user_fields结构
完整项目访问:https://github.com/zxy1013/flask_restful
利用其编写了一个newsapi接口,完整项目访问:https://github.com/zxy1013/flask_newsapi
跨域问题
来源于JavaScript的"同源策略",即只有协议+主机名+端口号 (如存在)相同,才允许相互访问。
也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。
跨域问题是针对JS和ajax的,html本身没有跨域问题。
后端解决跨域问题1
pip install flask-cors
# 使用第三方扩展 exts中创建
from flask_cors import CORS
cors= CORS()
# 与app进行绑定即可
cors.init_app(app=app,supports_credentials=True)
蓝图与api配合使用
user_bp = Blueprint('user', __name__)
# 蓝图绑定api 蓝图注册到app上
api = Api(user_bp)
app.register_blueprint(user_bp)
# 定义api
class xxxxApi(Resource):
pass
# 绑定路由
api.add_resource(xxxxApi,'/xxxx')
在OAuth协议中,token是在输入了用户名和密码之后获取的,利用这个token就可以拥有查看或者操作相应的资源的权限。
这些权限是因为服务器知道你是谁以后赋予你的,所以token其实就是你的一个“代表”。