第七课 Flask-Restful的使用
tags:
- Flask
- 2019千锋教育
categories:
- flask-Restful
- 返回值序列化
- RequestParser对象
文章目录
第一节 Flask-Restful介绍安装
-
官网:https://flask-restful.readthedocs.io/en/latest/index.html
-
安装:pip install Flask-RESTful -i https://pypi.douban.com/simple
-
restful api是用于在前端与后台进行通信的一套规范。使用这个规范可以让前后端开发变得更加轻松。以下将讨论这套规范的一些设计细节
-
协议:采用http或者https协议。
-
数据传输格式: 数据之间传输的格式应该都使用json,而不使用xml。
-
url链接:url链接中,不能有动词,只能有名词。并且对于一些名词,如果出现复数,那么应该在后面加s。比如:获取文章列表,应该使用
/articles/
,而不应该使用/get_article/ -
HTTP请求的方法:
- GET:从服务器上获取资源。
- POST:在服务器上新创建一个资源。
- PUT:在服务器上更新资源。(客户端提供所有改变后的数据)
- PATCH:在服务器上更新资源。(客户端只提供需要改变的属性)
- DELETE:从服务器上删除资源。
-
示例如下:
- GET /users/:获取所有用户。
- POST /user/:新建一个用户。
- GET /user/id/:根据id获取一个用户。
- PUT /user/id/:更新某个id的用户的信息(需要提供用户的所有信息)。
- PATCH /user/id/:更新某个id的用户信息(只需要提供需要改变的信息)。
- DELETE /user/id/:删除一个用户。
-
写一个Resource资源apis.py
# apis.py
from flask_restful import Api, Resource
api = Api()
def init_ api (app):
api.init_app(app)
class HelloResource(Resource):
def get(self):
return {"msg": "hello api get"}
def post(self):
return {"msg": "hello api post"}
# 注册HelloResource 通过
api.add_resource(HelloResource, '/hello/')
- 这里去掉了之前views的方式注册路由。通过127.0.0.1:5000/hello/,访问资源。
# __init__.py
from App.apis import init_api
# 只要路由相关的 都写在__init__.py中 不要卸载ext中
init_api(app=app)
- 把上面的apis.py 变成包的形式。用来写不同种类的接口。
- 除了Flask-Restful可以用来实现这个,flask还有其他的包也可以用来实现Restful接口。
- Flask-Restless https://restless.readthedocs.io/en/latest/ (测试版 bug多)
- Flask-REST-JSONAPI https://flask-rest-jsonapi.readthedocs.io/en/latest/
第二节 Flask-Restful返回数据序列化
- 序列化: 直接调用marshal函数或者通过marshal_with装饰。它装饰内部也是调用marshal函数
- 直接marshal函数 “data”: marshal(goods, goods_fields)
- @marshal_with(goods_fields) 可以直接返回goods
- 想要返回data,data中嵌套goods: 写一个single_goods_fields如下。使用装饰器@marshal_with(single_goods_fields)
- goods_fields和single_goods_fields 实际上就是我们需要返回数据对象序列化的模板
- 模版中多个没有的字段 不会报错 返回null 比如: “hh”: fields.String
- 以格式的模板为主
- 如果格式和数据完全对应,数据就是预期格式
- 如果格式比数据中的字段多,程序依然正常运行,不存在的字段是默认值"desc": fields.String(default=‘success’),
- 如果格式比数据中的字段少,程序正常执行,少的字段不会显示
- 格式和数据的映射
- 格式中的字段名和数据中的名需要一致
- 不一致可以起别名解决:“name”: fields.String(attribute=‘g_name’)
- 返回URL:‘uri’: fields.Url(‘single_goods’, absolute=True) absolute 绝对路径
- 需要加上endpoint名称:api.add_resource(GoodsResource, “/goods/int:id/”, endpoint=“single_goods”)
- 模板中的字段fields
- Raw
- format
- output
- 调用
- 将数据传递进格式化工具的时候,先获取值output
- 再对值进行格式化 format
- String
- 继承Raw
- 将value进行格式化
- 转换成兼容格式的text
- Interger
- 继承自Raw
- 重写了初始化,将default设置为0
- 重写格式化 直接将value转换成int
- Boolean
- 继承自Raw
- 重写格式化
- 重写格式化,直接将value转换成 bool
- Nested
- 继承自Raw
- 重写output
- 进行marshal
- List
- 继承自Raw
- 重写output
- 判断你的类型
- 对不同的类型进行不同的处理
- dict 直接进行处理
- list 迭代处理
- 重写format
- 进行格式化
- Raw
# goods_api.py
from flask import request
from flask_restful import Resource, fields, marshal_with, marshal, abort, reqparse
from App.models import Goods
goods_fields = {
"id": fields.Integer,
"name": fields.String(attribute='g_name'),
"g_price": fields.Float,
'uri': fields.Url('single_goods', absolute=True)
}
single_goods_fields = {
"data": fields.Nested(goods_fields),
"status": fields.Integer,
"msg": fields.String,
}
multi_goods_fields = {
"status": fields.Integer,
"msg": fields.String,
"data": fields.List(fields.Nested(goods_fields)),
"desc": fields.String(default='success'),
"number":fields.Integer
}
parser = reqparse.RequestParser()
parser.add_argument("g_name", type=str, required=True, help="please input g_name")
parser.add_argument("g_price", type=float, help="please input number")
parser.add_argument("mu", action="append")
parser.add_argument('rname', dest="name")
parser.add_argument("OUTFOX_SEARCH_USER_ID_NCOO", dest="lo", action="append", location=["cookies", "args"])
parser.add_argument("User-Agent",dest="ua", location="headers")
class GoodsListResource(Resource):
# @marshal_with(multi_goods_fields)
def get(self):
args = parser.parse_args()
print(args.get("lo"))
print(args.get("ua"))
goods_list = Goods.query.all()
data = {
"status": 200,
"msg": "ok",
"data": goods_list,
'desc': "Get Ok"
}
return marshal(data, multi_goods_fields)
@marshal_with(single_goods_fields)
def post(self):
# g_name = request.form.get('g_name')
# g_price = request.form.get('g_price')
args = parser.parse_args()
g_name = args.get('g_name')
g_price = args.get('g_price')
print(args.get('mu'))
print(args.get('name'))
goods = Goods()
goods.g_name = g_name
goods.g_price = g_price
if not goods.save():
abort(400)
"""
JSON
Response
格式
单个对象
{
"status": 200,
"msg" : "ok",
"data" :{
"property": "value",
"property": "value",
"property": "value",
}
}
多个对象,列表对象
{
"status": 200,
"msg" : "ok",
"data" : [
{
"property": "value",
"property": "value",
"property": "value"
},
{
"property": "value",
"property": "value",
"property": "value"
},
{
"property": "value",
"property": "value",
"property": "value"
},
]
}
"""
data = {
"msg": "create success",
"status": 201,
# "data": marshal(goods, goods_fields)
"data": goods
}
return data
class GoodsResource(Resource):
@marshal_with(single_goods_fields)
def get(self, id):
goods = Goods.query.get(id)
data = {
"status": 200,
"msg": "ok",
"data": goods
}
return data
def delete(self, id):
goods = Goods.query.get(id)
if not goods:
# abort(404)
abort(404, message="goods doesn't exist", msg="fail")
if not goods.delete():
abort(400)
data = {
"msg": "delete success",
"status": 204
}
return data
def put(self, id):
goods = Goods.query.get(id)
if not goods:
abort(404)
g_price = request.form.get('g_price')
g_name = request.form.get('g_name')
goods.g_price = g_price
goods.g_name = g_name
if not goods.save():
abort(400)
data = {
"msg": "put ok",
"status": 201,
"data": goods
}
return marshal(data, single_goods_fields)
@marshal_with(single_goods_fields)
def patch(self, id):
goods = Goods.query.get(id)
if not goods:
abort(404)
g_price = request.form.get('g_price')
g_name = request.form.get('g_name')
goods.g_price = g_price or goods.g_price
goods.g_name = g_name or goods.g_name
if not goods.save():
abort(400)
data = {
"msg": "put ok",
"status": 201,
"data": goods
}
return data
#models.py
from App.ext import db
class BaseModel(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
def save(self):
try:
db.session.add(self)
db.session.commit()
return True
except Exception as e:
print(e)
return False
def delete(self):
try:
db.session.delete(self)
db.session.commit()
return True
except Exception as e:
print(e)
return False
class Goods(BaseModel):
g_name = db.Column(db.String(64))
g_price = db.Column(db.Float, default=0)
第三节 Flask-Restful RequestParser对象
- RequestParser使用过程
- 先定义一个RequestParser对象
- 向对象中添加字段
- 从对象中获取字段
- 对象在添加参数的时候,可以实现数据预校验
- 参数是否必须
- 数据的类型
- 还可以设置错误提示
- 接收多个值 action=“append”
- 也可以在接收的时候指定别名
- location 可以指定参数的来源
parser = reqparse.RequestParser()
# 可以帮我们校验前端数据 required=True
parser.add_argument("g_name", type=str, required=True, help="please input g_name")
# 校验数据类型
parser.add_argument("g_price", type=float, help="please input number")
# 接收多个值 比如复选框
parser.add_argument("mu", action="append")
# 前端传rname 后台接收name
parser.add_argument('rname', dest="name")
# 指定数据来源为 cookies或者args 可以指定一个或多个。
# 追加说明 拿多个值放到列表中
parser.add_argument("OUTFOX_SEARCH_USER_ID_NCOO", dest="lo", action="append", location=["cookies", "args"])
parser.add_argument("User-Agent",dest="ua", location="headers")
# 获取参数
args = parser.parse_args()
g_name = args.get('g_name')
g_price = args.get('g_price')
# 返会多个值的列表
print(args.get('mu'))
print(args.get('name'))
第四节 爬虫与反爬虫
- 基于IP频率反爬
- 客户端使用代理服务器
- 基于UA
- 使用UA池
- 基于Cookie或用户反爬
- Cookie池
- 登陆大量账号,存储cookie