前后端分离和前后端不分离
前后端不分离
在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高。
这种应用模式比较适合纯网页应用,但是当后端对接App时,App可能并不需要后端返回一个HTML网页,而仅仅是数据本身,所以后端原本返回网页的接口不再适用于前端App应用,为了对接App后端还需再开发一套接口。
前后端分离
在前后端分离的应用模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果。至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自己决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。
在前后端分离的应用模式中 ,前端与后端的耦合度相对较低。
在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。
一、RESTful定义
RESTful
是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTful
适用于移动互联网厂商作为业务使能接口的场景。
RESTFUL特点包括:
1、每一个URI代表1种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET
用来获取资源,POST
用来新建资源(也可以用于更新资源),PUT
用来更新资源,DELETE
用来删除资源;
3、通过操作资源的表现形式来操作资源;
4、资源的表现形式是XML
或者 JSON
;
5、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
二、RESTful风格的软件架构
RESTful架构是对MVC架构改进后所形成的一种架构,通过使用事先定义好的接口与不同的服务联系起来。在RESTful架构中,客户端使用POST
,DELETE
,PUT
和GET
四种请求方式分别对指定的URL
资源进行增删改查操作。因此,RESTful是通过URI实现对资源的管理及访问,具有扩展性强、结构清晰的特点。
RESTful架构将服务器分成前端服务器和后端服务器两部分,前端服务器为用户提供无模型的视图;后端服务器为前端服务器提供接口。浏览器向前端服务器请求视图,通过视图中包含的AJAX函数发起接口请求获取模型。
项目开发引入RESTful架构,利于团队并行开发。在RESTful架构中,将多数HTTP请求转移到前端服务器上,降低服务器的负荷,使视图获取后端模型失败也能呈现。但RESTful架构却不适用于所有的项目,当项目比较小时无需使用RESTful架构,项目变得更加复杂。
三、安装和使用
1.安装
pip install flask-restful
2.普通使用
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
# 需求,对外提供一个API接口,可以访问某个资源
# 步骤一:创建restful的API
api = Api(app)
# 步骤二,定义资源resource
class HelloResource(Resource):
# 定义各种操作(函数)
def get(self):
return {'get': 'get'}
def put(self):
return {'put': 'put'}
def post(self):
return {'post': 'post'}
# 步骤三:把资源加载到Api中才能对外发布
api.add_resource(HelloResource, '/hello')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)
2.蓝图使用
# -*- coding: utf-8 -*-
from flask import Flask, Blueprint
from flask_restful import Api, Resource
bp = Blueprint('user', __name__)
app = Flask(__name__)
# 需求,对外提供一个API接口,可以访问某个资源
# 步骤一:创建restful的API
api = Api(bp)
# 步骤二,定义资源resource
class HelloResource(Resource):
# 定义各种操作(函数)
def get(self):
return {'get': 'get'}
def put(self):
return {'put': 'put'}
def post(self):
return {'post': 'post'}
# 步骤三:把资源加载到Api中才能对外发布
api.add_resource(HelloResource, '/hello')
# 注册蓝图
app.register_blueprint(bp, url_prefix='/user')
# 注意:当前情况下api的接口访问地址 = 蓝图的url_prefix + 资源请求的路径
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)
注意:如果蓝图里面有url_prefix
,那么请求url = url_prefix + resource_url
四、Request和RequestParser类
1、RequestParser类
Flask-RESTful 提供了 RequestParser 类,用来帮助我们检验和转换请求数据。
from flask_restful import reqparse
def post(self):
# 1.创建请求参数校验的对象requestParser
rq = reqparse.RequestParser()
# 2.定义参数的校验申明
rq.add_argument('a', required=True, location='args')
# 3.启动校验
req = rq.parse_args()
# 4.校验完后得到参数的值
a = req.a
return {'post': 'post', "a": a}
使用步骤:
- 创建请求参数校验
RequestParser
对象。 - 向
RequestParser
对象中添加需要检验或转换的参数声明。 - 使用
parse_args()
方法启动检验处理。 - 检验之后从检验结果中获取参数时可按照字典操作或对象属性操作。
2、参数说明
1、required
描述请求是否一定要携带对应参数,默认值为False
-
True 强制要求携带
若未携带,则校验失败,向客户端返回错误信息,状态码400 -
False 不强制要求携带
若不强制携带,在客户端请求未携带参数时,取出值为None
2、help
参数检验错误时返回的错误描述信息
3、action
描述对于请求参数中出现多个同名参数时的处理方式
action='store'
保留出现的第一个, 默认
action='append'
以列表追加保存所有同名参数的值
4、choices
rq.add_argument('c', type=str, choices=['男', '女'], action='append', location='args')
5、type
描述参数应该匹配的类型,可以使用python的标准数据类型string、int,也可使用Flask-RESTful提供的检验方法,还可以自己定义。
标准类型
rq.add_argument('b', type=str, required=True, action='append', location='args')
Flask-RESTful提供
检验类型方法在 flask_restful.inputs
模块中
- url
regex(指定正则表达式)
- natural 自然数0、1、2、3…
- positive 正整数 1、2、3…
int_range(low ,high)
整数范围- boolean 布尔类型
6、location
描述参数应该在请求数据中出现的位置
# Look only in the POST body 表单
parser.add_argument('name', type=int, location='form')
# Look only in the querystring 请求地址?后面的参数
parser.add_argument('PageSize', type=int, location='args')
# From the request headers 请求头
parser.add_argument('User-Agent', location='headers')
# From http cookies
parser.add_argument('session_id', location='cookies')
# From json
parser.add_argument('user_id', location='json')
# From file uploads 文件提交
parser.add_argument('picture', location='files')
也可指明多个位置,中括号 [ ]
parser.add_argument('text', location=['headers', 'json'])
# -*- coding: utf-8 -*-
from flask import Flask
from flask_restful import Api, Resource, reqparse, inputs
app = Flask(__name__)
# 需求,对外提供一个API接口,可以访问某个资源
# 步骤一:创建restful的API
api = Api(app)
# 步骤二,定义资源resource
class HelloResource(Resource):
# 定义各种操作(函数)
def get(self):
return {'get': 'get'}
def put(self):
return {'put': 'put'}
def post(self):
# 1.创建请求参数校验的对象requestParser
rq = reqparse.RequestParser()
# 2.定义参数的校验申明
rq.add_argument('a', type=int, required=True, help='参数a错误', location='args')
# 如果定义help,那么所有的校验只有一种
rq.add_argument('b', type=str, required=True, action='append', location='args')
rq.add_argument('c', type=str, choices=['男', '女'], action='append', location='args')
rq.add_argument('d', type=inputs.regex('^\d{2}$'), location='args') # 只允许两位整数
rq.add_argument('e', type=inputs.int_range(1, 100), location='args') # 允许范围内
rq.add_argument('f', type=inputs.boolean) # 只允布尔类型
# 3.启动校验
req = rq.parse_args()
# 4.校验完后得到参数的值
a = req.a
b = req.b
c = req.c
d = req.d
return {"a": a, 'b': b, 'c': c, 'd': d}
# 步骤三:把资源加载到Api中才能对外发布
api.add_resource(HelloResource, '/hello')
# app.run可以省略
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)
五、RESTful的响应处理
1、序列化数据
Flask-RESTful
提供了marshal
工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值。
from flask import Flask
from flask_restful import Api, Resource, fields, marshal, marshal_with
app = Flask(__name__)
# 需求,对外提供一个API接口,可以访问某个资源
# 步骤一:创建restful的API
api = Api(app)
class User(object):
def __init__(self, username, passwd, user_id):
self.uname = username
self.pwd = passwd
self.id = user_id
# 为了把模型对象转换为字典,在marshal里面必须定义一个属性转换格式
property_fields = {
'pwd': fields.String,
'uname': fields.String
}
# 步骤二,定义资源resource
class HelloResource(Resource):
# 定义各种操作(函数)
@marshal_with(fields=property_fields, envelope='user01')
def get(self): # 获取
u = User('zhang san', '123123', 1)
return u
def put(self): # 更新
return {'put': 'put'}
def post(self): # 新建资源
return {'post': 'post'}
# 步骤三:把资源加载到Api中才能对外发布
api.add_resource(HelloResource, '/hello')
# app.run可以省略
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)
也可以不使用装饰器
class HelloResource(Resource):
# @marshal_with(fields=property_fields, envelope='user01')
def get(self, **kwargs): # 获取
u = User('zhang san', '123123', 1)
return marshal(u, property_fields, envelope='user01')
2、定制返回的JSON格式
需求: 想要接口返回的JSON数据具有如下统一的格式
{"message": "描述信息", "data": {要返回的具体数据}}
在接口处理正常的情况下, message返回ok即可,但是若想每个接口正确返回时省略message字段
u = User('zhang san', '123123', 1)
对于诸如此类的接口,能否在某处统一格式化成上述需求格式?
{
"message": "OK",
"data": {
"pwd": "123123",
"uname": "zhang san"
}
}
解决
Flask_RESTfu
l的Api
对象提供了一个representation
的装饰器,允许定制返回数据的呈现格式
@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# 此处添加自己定义的json格式规则
if 'message' not in data:
data = {'message': 'OK',
'data': data}
Flask-RESTful原始对于json的格式处理方式如下:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal, marshal_with
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps
app = Flask(__name__)
# 需求,对外提供一个API接口,可以访问某个资源
# 步骤一:创建restful的API
api = Api(app)
class User(object):
def __init__(self, username, passwd, user_id):
self.uname = username
self.pwd = passwd
self.id = user_id
# 为了把模型对象转换为字典,在marshal里面必须定义一个属性转换格式
property_fields = {
'pwd': fields.String,
'uname': fields.String
}
@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# 此处添加自己定义的json格式规则
if 'message' not in data:
data = {'message': 'OK',
'data': data}
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
# 步骤二,定义资源resource
class HelloResource(Resource):
# 定义各种操作(函数)
# @marshal_with(fields=property_fields, envelope='user01')
def get(self, **kwargs): # 获取
u = User('zhang san', '123123', 1)
return marshal(u, property_fields)
def put(self): # 更新
return {'put': 'put'}
def post(self): # 新建资源
return {'post': 'post'}
# 步骤三:把资源加载到Api中才能对外发布
api.add_resource(HelloResource, '/hello')
# app.run可以省略
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888, debug=True)