0
点赞
收藏
分享

微信扫一扫

Flask-4Restful接口


REST API

前后端分离的最佳实践,是开发的一套标准或者是一套规范

  1. 每一个 URI 代表一种资源
  2. 客户端和服务器之间,传递这种资源的某种表现层
  3. 客户端通过四个 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

Flask-4Restful接口_python

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"
}
]

  1. 必须继承自 Raw
  2. 重写方法:
    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')

跨域

出现此报错即存在跨域问题

Flask-4Restful接口_flask_02

JavaScript 同源策略,只有 协议+主机名+端口 相同才允许访问,也就是 JS 子能访问和操作自己域下的资源,不能访问和操作其他域下的资源,跨域问题针对 JS 和 Ajax,html 本身没有跨域问题

跨域问题解决

  1. 响应头添加 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'

  1. jsonp 只支持 get 请求不支持 post 请求
  2. HttpClient 内部转发
  3. 使用接口网关–nginx、springcloud zuul

第三方扩展flask-cors

  1. 安装

pip install flask-cors

  1. 引入插件

# 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”>

index



举报

相关推荐

0 条评论