Flask-4Restful接口
REST API前后端分离的最佳实践,是开发的一套标准或者是一套规范每一个 URI 代表一种资源客户端和服务器之间,传递这种资源的某种表现层客户端通过四个 HTTP 动词(GET,POST,PUT,DELETE)对服务器端资源进行操作,实现 表现层状态转化Restful基础安装pip install flask-restful使用ext/__init__.pyfrom flask_sqlalche
REST API
前后端分离的最佳实践,是开发的一套标准或者是一套规范
- 每一个 URI 代表一种资源
- 客户端和服务器之间,传递这种资源的某种表现层
- 客户端通过四个 HTTP 动词(GET,POST,PUT,DELETE)对服务器端资源进行操作,实现 表现层状态转化
Restful基础
安装
pip install flask-restful
使用
ext/__init__.py
from flask_sqlalchemy import SQLAlchemy
from flask_restful import Api
db = SQLAlchemy()
# 1.创建 api 对象
api = Api()
apps/__init__.py
# 2.绑定 app
api.init_app(app=app)
view.py
from flask import Blueprint, request, render_template, flash, redirect, url_for
from flask_restful import Resource
from ext import api
user_bp = Blueprint('user', __name__, url_prefix='/api')
# 3.定义类视图
class UserResource(Resource):
def get(self):
return {'msg': '------------>get'}
def post(self):
pass
def put(self):
pass
def delete(self):
pass
# 4.绑定类视图,user为路由
api.add_resource(UserResource,'/user')
# url_map
Map([<Rule '/user' (HEAD, PUT, OPTIONS, GET, DELETE, POST) -> userresource>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])
参数传递
数据格式化
view.py
# 格式化输出需要提前规范格式,字段同数据库,可少不可多
user_fields = {
'id': fields.Integer,
'username': fields.String,
'password': fields.String,
'udatetime': fields.DateTime
}
class UserResource(Resource):
@marshal_with(user_fields) # 将 user 按照 user_fields 的格式转化成一个序列化对象
def get(self):
user = User.query.all()
return user # user不是 str,list,int,不能直接返回,需要使用 @marshal——with
def post(self):
pass
def put(self):
pass
def delete(self):
pass
endpoint
api.add_resource(UserResource, '/user', endpoint='alluser')
api.add_resource(UserSimpleResource, '/user/<int:uid>')
# url_map
Map([<Rule '/user' (DELETE, GET, PUT, POST, HEAD, OPTIONS) -> alluser>,
<Rule '/static/<filename>' (GET, OPTIONS, HEAD) -> static>,
<Rule '/user/<uid>' (DELETE, GET, PUT, POST, HEAD, OPTIONS) -> usersimpleresource>])
格式化输入RequestParser
对传输参数进行解析限制,类似于 wtform 提供的功能
# 参数解析
parser = reqparse.RequestParser() # 创建解析对象
# location 限制只能通过 form 提交
parser.add_argument('username', type=str, required=True, help='必须输入用户名', location=['form'])
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help='必须输入密码', location=['form'])
parser.add_argument('hobby', action='append') # ['篮球','足球','游戏']
parser.add_argument('icon', type=FileStorage, location=['files'])
# required = True 代表为必填,location 代表只能通过 post 传参,help 为提示信息
# 上传文件,postman 需要使用 form-data 字段
......
class UserResource(Resource):
@marshal_with(user_fields) # 将 user 按照 user_fields 的格式转化成一个序列化对象
def get(self):
user = User.query.all()
return user # user不是 str,list,int,不能直接返回,需要使用 @marshal——with
def post(self):
args = parser.parse_args()
username = args.get('username')
password = args.get('password')
boddy = args.get('hobby')
icon = args.get('icon')
print(icon)
if icon:
save_path = os.path.join(Config.UPLOAD_ICON_DIR, icon.filename)
icon.save(save_path)
......
数据输出
Output Fields
# 格式化输出需要提前规范格式
user_fields = {
'id': fields.Integer,
'uid': fields.String(attribute='username', default='anony'),
'password': fields.String,
'udatetime': fields.DateTime
}
# 前端能看到的是:id,username,password,udatetime,如果不希望前端看到真是的字段名,可以使用 attribute = 模型中的字段名,前边的名字可以随意取了
自定义 Fields
class IsDelete(fields.Raw):
def format(self, value):
print('---------------------',value)
return 'yes' if value else 'no'
# 格式化输出需要提前规范格式
user_fields = {
'isdel':IsDelete(attribute='isdelete'),
}
# postman输出
[
{
"id": 1,
"uid": "xiaoli",
"password": "111111",
"isdel": "no",
"udatetime": "Sat, 20 Feb 2021 21:18:42 -0000"
},
{
"id": 2,
"uid": "xiaoming",
"password": "1234565",
"isdel": "yes",
"udatetime": "Sat, 20 Feb 2021 21:19:46 -0000"
}
]
-
必须继承自 Raw
-
重写方法:
def format(self):
return 结果
URL
适用于有一个列表,点击某一项获取详情页面
定义两个 user_fields
user_fields_1 = {
'id': fields.Integer,
'url': fields.Url('singleuser', absolute=False)
}
user_fields = {
'id': fields.Integer,
'uid': fields.String(attribute='username', default='anony'),
'password': fields.String,
'isdel': IsDelete(attribute='isdelete'),
'udatetime': fields.DateTime
}
.....
class UserResource(Resource):
@marshal_with(user_fields_1)
def get(self):
user = User.query.all()
return user
def post(self):
args = parser.parse_args()
username = args.get('username')
password = args.get('password')
boddy = args.get('hobby')
icon = args.get('icon')
print(icon)
if icon:
save_path = os.path.join(Config.UPLOAD_ICON_DIR, icon.filename)
icon.save(save_path)
def put(self):
pass
def delete(self):
pass
class UserSimpleResource(Resource):
@marshal_with(user_fields)
def get(self, id):
user = User.query.get(id)
return user
def post(self, id):
pass
def put(self, id):
pass
def delete(self, id):
pass
api.add_resource(UserResource, '/user', endpoint='alluser')
api.add_resource(UserSimpleResource, '/user/<int:id>', endpoint='singleuser')
复杂结构输出
json格式化:https://www.bejson.com/
套叠结构
第一种方法:marshal()
class UserFriendsResource(Resource):
# 不需要装饰器
def get(self, id):
friends_list = Friends.query.filter(Friends.uid == id).all()
user = User.query.get(id)
friends = []
for friend in friends_list:
friends.append(User.query.get(friend.id))
data = {
'username': user.username,
'nums': len(friends_list),
'friends': marshal(friends_list, user_fields)
# 把 friends_list 以 user_fields 定义的格式套叠输出
}
return data
api.add_resource(UserFriendsResource, '/friend/<int:id>')
第二种方式:marshal_with()
user_friends_fields = {
'username': fields.String,
'nums': fields.Integer,
'friends': fields.List(fields.Nested(user_fields))
# 把 friends_list 以 user_fields 定义的格式套叠列表输出
}
class UserFriendsResource(Resource):
@marshal_with(user_friends_fields)
def get(self, id):
friends_list = Friends.query.filter(Friends.uid == id).all()
user = User.query.get(id)
friends = []
for friend in friends_list:
friends.append(User.query.get(friend.id))
data = {
'username': user.username,
'nums': len(friends_list),
'friends': friends_list
}
return data
api.add_resource(UserFriendsResource, '/friend/<int:id>')
两种方式实现的效果相同
注意:data 必须是符合 json 格式
实例
模型继承
基模型
# apps/models/__init__.py
from datetime import datetime
from exts import db
class BaseModel(db.Model):
__abstract__ = True
# 作为抽象类,只能被继承,不能单独使用
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
datetime = db.Column(db.DateTime, default=datetime.now)
子模型
# apps/models/user_models.py
class NewsType(BaseModel):
__tablename__ = 'news_type'
type_name = db.Column(db.String(20), nullable=False)
API结合蓝图
from flask import Blueprint
from flask_restful import Api, marshal_with, fields, Resource
from apps.models.user_models import News, NewsType
news_bp = Blueprint('news', __name__)
api = Api(news_bp)
# 继承蓝图
types_fields = {
'id': fields.Integer,
'name': fields.String(attribute='type_name')
}
class NewsSearchResource(Resource):
@marshal_with(types_fields)
# 特别注意 括号里没有引号
def get(self):
news_list = NewsType.query.all()
return news_list
api.add_resource(NewsSearchResource, '/news')
跨域
出现此报错即存在跨域问题
JavaScript 同源策略,只有 协议+主机名+端口 相同才允许访问,也就是 JS 子能访问和操作自己域下的资源,不能访问和操作其他域下的资源,跨域问题针对 JS 和 Ajax,html 本身没有跨域问题
跨域问题解决
-
响应头添加 Header 允许访问
response = make_response() response.headers['Access-Control-Allow-Origin']='*' response.headers['Access-Control-Allow-Methods']='GET,POST' response.headers['Access-Control-Allow-Headers']='x-request-with,Content-type'
-
jsonp 只支持 get 请求不支持 post 请求
-
HttpClient 内部转发
-
使用接口网关–nginx、springcloud zuul
第三方扩展flask-cors
-
安装
pip install flask-cors
-
引入插件
# exts/__init__.py from flask_cors import CORS cors = CORS() # apps/__init__. py def create_app(): app = Flask(__name__, template_folder='../') cors.init_app(app=app, supprots_credentials=True) ......
jQuery 动态创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<div id="container"></div>
</body>
<script>
$(function () {
url = 'http://127.0.0.1:5000/news';
$.get(url, function (data) {
for(i=0;i<=data.length;i++){
$('#container').append('<p>'+data[i].name+'</p>')
}
});
});
</script>
</html>
arset=“UTF-8”>
```
更多推荐
所有评论(0)