接口概念

  IOP:面向接口编程,不再关注具体的实现;只关注输入、输出。

  http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html

服务器返回数据类型:

  网页数据html,为浏览器使用

  Json数据,ajax javascript发请求的一种方式;也可以使用request的python请求方式

为移动端编写接口关注:

  接口地址是什么:/xxx/yyy/zzz

  接口需要什么参数:参数根据业务场景

  返回什么格式的数据:大多数是json数据

RestfulAPI:

   一种软件架构风格、设计风格、而不是标准,只是提供了一组设计原则和约束条件。它主要用户客户端和服务器交互类的软件。

  基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。REST全程是Representational State Transfer,表征性状态转移。

  首次在2000年Roy Thomas Fielding的博士论文中出现,Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者,

  Apache服务器软件的作者之一,Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了广泛的关注。

理解RESTful

介绍:https://github.com/RockTeach/PythonCourse/blob/master/web/flask/restful.md

  要理解RESTful架构,最好的就是去理解它的单词 Representational State Transfer 到底是什么意思,它的每一个词到底要表达什么。

  REST的释义,"(资源的)表现层状态转化",其实这省略了主语。“表现层”其实指的是“资源(Resource)”的“表现层”。

状态码

  服务器向用户返回的状态码和提示信息,常见的有以下一些地方

  • 200:OK - [GET]:服务器成功返回用户请求的数据
  • 201:CREATED -[POST/PUT/PATCH]:用户新建或修改数据成功
  • 202:Accepted - [*] :表示一个请求已经进入后台排队(异步任务)
  • 204:NO CONTENT - [DELETE]:表示数据删除成功
  • 400:INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误
  • 401:Unauthorized - [*] :表示用户没有权限(令牌,用户名,密码错误)
  • 403:Forbidden - [*]:表示用户得到授权,但是访问是被禁止的
  • 404:NOT FOUND - [*]:用户发出的请求针对的是不存在的记录
  • 406:Not Acceptable - [*]:用户请求格式不可得
  • 410:Gone - [GET] :用户请求的资源被永久移除,且不会再得到的
  • 422:Unprocesable entity -[POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误
  • 500:INTERNAL SERVER EROR - [*] :服务器内部发生错误

资源(Resource)

  所谓“资源”,就是网络上的一个实体,或者说是网络上的一个具体信息。

  它可以是一段文本,一张图片,一首歌曲,一种服务,总之就是一个具体的实例。

  你可以使用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。

  要获取这个资源,访问它的URI就可以了,因此URI就成了每一个资源的地址或独一无二的识别符。所谓“上网”就是与互联网上一系列的“资源”互动,调用它们的URI。

表现层(Representation)

  “资源”是一种信息实体,它可以有多种外在表现形式。我们把“资源”具体呈现出来的形式,叫做它的”表现层“(Representation)。

  URI只代表资源的实体,不代表它的形式。严格地说,有些网站最后的”.html“后缀名是不必要的,因为这个后缀表示格式,属于”表现层“范畴,而URI应该只代表”资源“的位置。

  它的具体表现形式,应该在HTTP请求头的信息中使用Accept和Content-Type字段指定。

状态转换(State Transfer)

  访问一个网站,就代表客户端和服务端的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

  互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务端。

  因此,如果客户端想要操作服务器,就必须通过某种手段,让服务器端发生”状态转换(State Transfer)“。

  而这种转换是建立在表现层之上的,所以就是”表现层状态转化“。

  客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议中,四个表示操作方式的动词:GET,POST,PUT,DELETE。

  它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可用于更新资源),PUT用来更新资源,DELETE用来删除资源

总结 :

  1. 每一个URI代表一种资源
  2. 客户端和服务器之间,传递这种资源的某种表现层
  3. 客户端通过四个HTTP动词,对服务端资源进行操作,实现”表现层状态转换“
  4. 同一个url针对用户的不同的请求操作,表现出来的状态是不同的。表现出来的多种形式,就是表现层状态转换。
  • RESTful是软件架构设计思想。使用在CS,客户端和服务端这种架构模型中。

  • 表现层状态转换

    • 主语 (资源)

    • URI 每个URI代表一种资源

    • 资源展现给我们的形式就叫做表现层

    • 通过HTTP的请求谓词来实现表现层转换

  • 重要概念

    • URI、HTTP请求谓词、JSON

注意:

  postman/前端 在向服务器提交json数据时,需要声明提交的类型。在postman的请求的headers增加content-type:application/json。

  flask 在确认请求数据是通过json提交后,会将json字符产转换成 字典。保存在request.json中

FBV简单体验:

  • views.py
 from flask import Blueprint, request

 restful_bp = Blueprint("restful_bp",__name__)
"""
动作 URL 状态码 数据库操作 含义
GET /posts 200 SELECT 从 blog_posts 表中查询一组数据
POST /posts 201 INSERT 向 blog_posts 表中插入一条数据 GET /posts/123 200 SELECT 从 blog_posts 表中查询 id 为123 的记录
PUT /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供全部字段的更新)
PATCH /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供部分字段的更新)
DELETE /posts/123 204 DELETE 删除 blog_posts 表中 id 为123 的记录
""" @restful_bp.route("/posts",methods = ["POST","GET"])
def post_list():
if request.method == "POST":
return "向 blog_posts 表中插入一条数据",201 # 201 表示创建成功
elif request.method == "GET":
return "从 blog_posts 表中查询一组数据",200 @restful_bp.route("/posts/<post_id>",methods=["GET","POST","PUT","PATCH","DELETE"])
def post_detail(post_id):
if request.method == "GET":
return "从 blog_posts 表中查询 id 为{} 的记录".format(post_id),200
elif request.method == "PUT":
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供全部字段的更新)".format(post_id),200
elif request.method == "PATCH":
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供部分字段的更新)".format(post_id),200
elif request.method == "DELETE":
return "删除 blog_posts 表中 id 为{} 的记录".format(post_id),204 # 204 表示删除成功
  •  models.py
 from flask_sqlalchemy import SQLAlchemy

 db = SQLAlchemy()

 class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False)
# 对象方法。将类的对象形式转化成字典形式
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'sort': self.sort,
}

FBV实现:创建POST | 查询GET

  • views.py

    • POST:http://127.0.0.1:5000/channel_dict
    • GET:http://127.0.0.1:5000/channel_dict
 @restful_bp.route('/channel_dict/',methods=["GET","POST"])
def channel_list():
if request.method == 'POST':
# # 结果:b'{\n\t"\xe5\x90\x8d\xe5\xad\x97":"\xe7\xa7\x91\xe6\x8a\x80",\n\t"sort":1\n}'
# # 需在postman中设置一下请求头headers。Content-Type:application/json
# print(request.data)
# # 结果:None
# print(request.json)
if 'name' not in request.json or 'sort' not in request.json:
return abort(400) channel = Channel()
channel.name = request.json['name']
channel.sort = request.json['sort'] db.session.add(channel)
db.session.commit() return jsonify({'channel':channel.to_dict()}),201 elif request.method == "GET":
# 数据库查询的返回的是list类型的数据
_channels = Channel.query.all()
# 将数据库中查询出的对象 转化成 字典形式
channels = [channel.to_dict() for channel in _channels]
# flask视图函数只能返回 str 或 response 对象。可以将数据库查询出来的转化成json,以满足flask视图函数的返回要求
ret = {
'channels':channels
}
# import json
# json_str = json.dumps(ret)
# return json_str
return jsonify(ret)

FBV实现:GET | PUT | PATCH | DELETE

  • views.py:http://127.0.0.1:5000/channels/2
 @restful_bp.route("/channels/<channel_id>",methods=["GET","POST","PUT","PATCH","DELETE"])
def channel_detail(channel_id):
    # 根据条件查询对象
channel = Channel.query.get(channel_id) # 获取信息:http://127.0.0.1:5000/channel
if request.method == "GET":
return jsonify({'channel':channel.to_dict()}),200 # 更新数据:全量更新
elif request.method == "PUT":
if 'name' not in request.json or 'sort' not in request.json:
return abort(400)
channel.name = request.json['name']
channel.sort = request.json['sort']
db.session.add(channel)
db.session.commit()
return jsonify({'channel':channel.to_dict()}),200 # 更新数据:差量更新
elif request.method == 'PATCH':
# name = channel.name
# if 'name' in request.json:
# name = request.json['name']
# channel.name = name
# 等价于 上四行代码。如果有数据更新就更新{先从json中获取数据,如果有就赋值给channel.name},没有数据更新就用原来的当做默认值
channel.name = request.json.get('name',channel.name)
channel.sort = request.json.get('sort',channel.sort)
db.session.commit()
return jsonify({'channel':channel.to_dict()}),200 # 删除数据。删除成功后返回的是空数据。
elif request.method == "DELETE":
db.session.delete(channel)
db.session.commit()
return "",204 

Flask-RestfulApi

框架优点:

  • 会让代码逻辑更加清晰,不会再有过多的if-else语句
  • 框架会提供参数的验证。像form一样提供验证数据的功能
  • 框架会提供丰富的输出格式,自定义输出的结构

框架注意:⚠️

  1. flask-restful 返回字典。框架内部自动转换为json。return {'channels':channels},200
  2. 如果在扩展文件ext.py实例化和注册路由资源,必须在加载时就注册好,不可以在调用函数懒加载时注册
  3. 根据请求的同名方法执行同名的视图函数,来完成不同的请求。request.method.lower() 获取请求方式然后将其变成小写

CBV简单体验:pip install Flask-RESTful  

    # method = request.method
# method = lower(method)
# 获取请求方式后将其转化为小写,然后去类中匹配,如果有同名的函数,就执行相应的请求方法函数 # Resource 是一个资源类,其实就是一个url
# Resource 的父类MethodView是flask自己的views中的一个类,不同Resource我们自己也可以完成各种请求。
# Resource 的父类MethodView也是根据不同的请求方法执行不同的请求函数,来完成各种请求操作
# 源码变小写 meth = getattr(self, request.method.lower(), None)。getattr是从一个实例中将类中的一个方法取出来赋值给一个变量,执行这个变量就是执行此函数方法

CBV类视图

 from flask import Flask
from flask_restful import Api, Resource app = Flask(__name__)
api = Api(app)
"""
动作 URL 状态码 数据库操作 含义
GET /posts 200 SELECT 从 blog_posts 表中查询一组数据
POST /posts 201 INSERT 向 blog_posts 表中插入一条数据
"""
class PostList(Resource):
"""
flask-restful 的所有请求是通过 类 来处理的。继承自Resource类。
flask-restful 框架内部会自动根据请求的HTTP METHOD调用同名的实例方法
GET: /posts -> def get(self): pass
POST:/posts -> def post(self): pass
"""
def get(self):
return "从 blog_posts 表中查询一组数据",200 def post(self):
return "向 blog_posts 表中插入一条数据",201 """
动作 URL 状态码 数据库操作 含义
GET /posts/123 200 SELECT 从 blog_posts 表中查询 id 为123 的记录
PUT /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供全部字段的更新)
PATCH /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供部分字段的更新)
DELETE /posts/123 204 DELETE 删除 blog_posts 表中 id 为123 的记录
"""
class PostDetail(Resource):
"""
flask-restful 框架内部也支持 URL 上配置的路由参数,路由参数会传递到具体处理请求的实例方法中
"""
def get(self,post_id):
return "从 blog_posts 表中查询 id 为{} 的记录".format(post_id),200 def put(self,post_id):
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供全部字段的更新)".format(post_id),200 def patch(self,post_id):
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供部分字段的更新)".format(post_id),200 def delete(self,post_id):
return "删除 blog_posts 表中 id 为{} 的记录".format(post_id),204 # 路由配置。参数[前两个参数必填]:(resource类(资源类)、访问的url地址、endpoint="相当于一个路由名称 如:blue.login。可以在url_for中使用")
api.add_resource(PostList,'/posts',endpoint='post_list') # 如果不定义endpoint参数,默认endpoint的值是 类名
api.add_resource(PostDetail,'/posts/<int:post_id>',endpoint='post_detail') if __name__ == '__main__':
app.run(debug=True)
 # 非restfulAIPI中不用装饰器的写法
# @app.route("/")
def index():
return "Index" # 参数:路由、描述、资源
app.add_url_rule("/index/","index",index) if __name__ == '__main__':
app.run(debug=True)

非API非装饰器写法

简单拆分格式:

  • manage.py、ext.py、models.py
from flask_script import Manager
from flask_migrate import MigrateCommand
from app import create_app app = create_app()
manager = Manager(app)
manager.add_command("db",MigrateCommand) if __name__ == '__main__':
manager.run()

manage.py

import os
from flask_migrate import Migrate
from app.models import db migrate = Migrate() def init_db(app):
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path, 'sqlite3.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app=app) def init_migrate(app):
migrate.init_app(app=app,db=db)

ext.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False)
# 对象方法。将类的对象形式转化成字典形式
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'sort': self.sort,
}

models.py

  • __innit__.py
 from flask import Flask
from flask_restful import Api
from app import ext
from app.apis import ChannelList, ChannelDetail
from app.views import restful_bp def create_app():
app = Flask(__name__) ext.init_db(app)
ext.init_migrate(app) # 注册实例化api扩展
api = Api(app)
# 路由注册
api.add_resource(ChannelList, '/api/ChannelList', endpoint='ChannelList')
api.add_resource(ChannelDetail, '/api/ChannelDetail/<int:id>',endpoint='ChannelDetail')
# 蓝图注册
app.register_blueprint(blueprint=restful_bp)
return app
  • api.py 初级[在models.py中定义函数用来字段转字典格式]
 from flask_restful import Resource,reqparse
from app.models import Channel, db # 输出格式化。字段自定义验证函数。自定义验证完成后,必须把验证通过后的数值返回出去以方便后续保存操作
def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value # 输入的验证。flask-restful通过reqparse包中的RequestParser类提供客户端请求的数据参数(通常是json)验证校验功能
# 验证需要的参数(客户端提交的需要验证的字段名称,required=True表示必填项,类型,help="错误提示信息")
channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name) # type='自定义验证函数名'
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') # http://127.0.0.1:5000/api/ChannelList [get请求、post请求]
class ChannelList(Resource):
"""
GET /channels
POST /channels
"""
def get(self):
# 数据库查询返回的是list类型的数据。需要将从数据库查询到的数据转为字典形式
_channels = Channel.query.all()
channels = [channel.to_dict() for channel in _channels]
# flask中视图函数只能返回str或者response对象。
# flask-restful可以将数据库中查询出来的数据转换成json;以满足flask视图函数的返回要求
# flask-restful返回字典,框架内部自动转换为json。return {'channels':channels},200
res = {
'channels':channels
}
return res,200 def post(self):
# 通过RequestParser的实例对象 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
     # flask-restful 的类视图方法只能返回字典格式
return {'channel':channel.to_dict()},201
  • api.py 高级[使用装饰器转字典]
 from flask_restful import Resource, reqparse, fields, marshal_with, abort
from app.models import Channel, db def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value
channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name)
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') # 通过marshal_with和fields一起完成自定义输出的功能,让api接口可以返回对象,系统内部将对象根据fields的定义格式转换成字段类型
channel_fields = {
"id":fields.Integer,
# url链接。"url": "http://127.0.0.1:5000/api/ChannelDetail/3",
"url":fields.Url(endpoint="ChannelDetail",absolute=True), # absolute=True 表示加上前缀http://127.0.0.1:5000
"name":fields.String,
"sort":fields.Integer,
}
 # http://127.0.0.1:5000/api/channels  可以get、post请求
class ChannelList(Resource): @marshal_with(fields=channel_fields)
def get(self):
_channels = Channel.query.all()
return _channels, 200
# 原来写法
# channels = [channel.to_dict() for channel in _channels]
# res = {
# 'channels':channels
# }
# return res,200 # 此装饰器接收一个参数(以什么格式)。有了这个装饰器,在返回的时候对象会按照参数同名所设置的方式变成一个字典返回
@marshal_with(fields=channel_fields)
def post(self):
# 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
# 原来返回方式
# return {'channel':channel.to_dict()},201
# flask-restful只能返回字典格式类型。添加装饰器marshal_with可以定制输出格式。
return channel,201
 # http://127.0.0.1:5000/api/ChannelDetail/3
class ChannelDetail(Resource):
"""
GET /channels/123
PUT /channels/234
PATCH /channels/123
DELETE /channels/123
"""
def get_object(self,id):
channel = Channel.query.get(id)
if channel is None:
return abort(404,message="找不到对象")
return channel @marshal_with(fields=channel_fields)
def get(self,id):
channel = self.get_object(id)
return channel,200 @marshal_with(fields=channel_fields)
def put(self,id):
channel = self.get_object(id)
args = channel_parser.parse_args() # 字段验证 channel.name = args.get("name",channel.name)
channel.sort = args.get("sort",channel.sort)
db.session.commit()
return channel,200 def patch(self,id):
self.put(id) def delete(self,id):
channel = self.get_object(id)
db.session.delete(channel)
db.session.commit()
return "",204


API 1:N 模型

  坑:'/api/ArticleDetails/<int:id>' 此id必须和数据库中的相应字段同名,否则匹配不上;.Nested:表示将一个比较复杂的数据对象拆分开,方便转为字典格式

 from flask import Flask
from flask_restful import Api
from app import ext
from app.apis import ChannelList, ChannelDetail, ArticleList, ArticleDetail
from app.views import restful_bp def create_app():
app = Flask(__name__) ext.init_db(app)
ext.init_migrate(app) # 注册实例化api扩展
api = Api(app)
# 注册频道路由
api.add_resource(ChannelList, '/api/ChannelLists', endpoint='ChannelLists')
api.add_resource(ChannelDetail, '/api/ChannelDetails/<int:id>',endpoint='ChannelDetails')
# 注册文章路由
api.add_resource(ArticleList, '/api/Articles', endpoint='Articles')
api.add_resource(ArticleDetail, '/api/ArticleDetails/<int:id>',endpoint='ArticleDetails') # 蓝图
app.register_blueprint(blueprint=restful_bp)
return app

__init__.py

 import datetime
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() # 频道 1
class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False) articles = db.relationship('Article',backref='channel',lazy='dynamic') # 文章 N
class Article(db.Model):
__tablename__ = "articles" id = db.Column(db.Integer,primary_key=True)
created_at = db.Column(db.DateTime,default=datetime.datetime.now())
updated_at = db.Column(db.DateTime,default=datetime.datetime.now(),onupdate=datetime.datetime.now())
title = db.Column(db.String(256),nullable=False)
content = db.Column(db.String(5000),nullable=False) channel_id = db.Column(db.Integer,db.ForeignKey("channels.id"))

models.py

 import datetime
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from app.models import Channel, db, Article # ============================ N ===================================
# 自定义一个类,用于时间格式化
class MyDTFmt(fields.Raw):
def format(self, value):
return datetime.datetime.strftime(value,'%Y-%m-%d %H:%M:%S') # 定义参数验证格式
article_parser = reqparse.RequestParser()
article_parser.add_argument('title',required=True,type=str,help="标题必填")
article_parser.add_argument('content',required=True,type=str,help="正文必填")
article_parser.add_argument('channel_id',required=True,type=int,help="频道必填") # 定义返回输出格式
article_fields = {
'id':fields.Integer,
'url':fields.Url(endpoint='ArticleDetails',absolute=True),
'title':fields.String,
'content':fields.String,
# 等同于:'channel':fields.Nested(channel_fields),
'channel':fields.Nested({ # 通过Nested将对象解开
'name':fields.String,
'url':fields.Url(endpoint="ChannelDetails",absolute=True),
"sort": fields.Integer,
}),
'created_at':MyDTFmt, # 进行自定义时间格式化
'updated_at':fields.DateTime(dt_format="iso8601") } # 文章模块。get、post
class ArticleList(Resource): @marshal_with(fields=article_fields)
def get(self):
articles = Article.query.all()
return articles,200 @marshal_with(fields=article_fields)
def post(self):
args = article_parser.parse_args() article = Article()
article.title = args.get('title')
article.content = args.get('content')
article.channel_id = args.get('channel_id') db.session.add(article)
db.session.commit()
return article,201 # 文章模块。get、put、patch、delete
class ArticleDetail(Resource):
def get_object(self,id):
article = Article.query.get(id)
if article is None:
return abort(404,message="找不到对象")
return article @marshal_with(fields=article_fields)
def get(self,id):
article = self.get_object(id)
return article,200 def put(self,id):
pass
def patch(self,id):
pass
def delete(self,id):
pass # ================================== 1 =========================================
# 自定义字段验证函数。 自定义验证后,必须把验证通过后的数值返回出去
def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name) # type=自定义验证函数名
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') channel_fields = {
"id":fields.Integer,
"url":fields.Url(endpoint="ChannelDetails",absolute=True),
"name":fields.String,
"sort":fields.Integer,
} channel_article_fields = {
"id": fields.Integer,
"url": fields.Url(endpoint="ChannelDetails",absolute=True),
"name": fields.String,
"articles": fields.List(fields.Nested(article_fields)) # 列表是一个articles对象,通过Nested将其解开,输出出去。
} # 频道模块。get、post
class ChannelList(Resource): @marshal_with(fields=channel_fields)
def get(self):
_channels = Channel.query.all()
return _channels, 200 # 此装饰器接收一个参数(以什么格式)。有了这个装饰器,在返回的时候对象会按照同名参数设置的方式变成一个字典返回
@marshal_with(fields=channel_fields)
def post(self):
# 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
return channel,201 # 频道模块。get、put、patch、delete
class ChannelDetail(Resource):
"""
GET /channels/123
PUT /channels/234
PATCH /channels/123
DELETE /channels/123
"""
def get_object(self,id):
channel = Channel.query.get(id)
if channel is None:
return abort(404,message="找不到对象")
return channel @marshal_with(fields=channel_article_fields)
def get(self,id):
channel = self.get_object(id) # channel.articles 代表了当前频道下文章列表
return channel,200 @marshal_with(fields=channel_fields)
def put(self,id):
channel = self.get_object(id)
args = channel_parser.parse_args() # 字段验证 channel.name = args.get("name",channel.name)
channel.sort = args.get("sort",channel.sort)
db.session.commit()
return channel,200 def patch(self,id):
self.put(id) def delete(self,id):
channel = self.get_object(id)
db.session.delete(channel)
db.session.commit()
return "",204

高级拆分格式:

    

  • manage.py
 """ manage.py """

 import os
from flask_migrate import MigrateCommand
from flask_script import Manager
from day05_chaiAdvanced import create_app # 从系统环境中获取参数FLASK_ENV的值给env,判断是什么环境下的服务器
# 避免外人修改代码,方便代码放在什么环境服务器下,就在什么环境下运行
# 在系统终端 vim .bashrc 编写系统环境变量:#FLASK_ENV。export FLASK_ENV = "develop" 保存退出
env = os.environ.get("FLASK_ENV") or 'default' # 首先创建一个flask对象、加载配置、加载扩展库、初始化路由
app = create_app(env) # flask-scripy扩展
manager = Manager(app)
manager.add_command("db",MigrateCommand) if __name__ == '__main__':
manager.run()
  • __init__.py
 ''' 启动项目的__init__.py '''
from flask import Flask from day05_chaiAdvanced.settings import Config, envs
from day05_chaiAdvanced.extension import init_ext
from day05_chaiAdvanced.views import init_api def create_app(env): # 创建Flask对象
app = Flask(__name__,template_folder="../templates") # 加载初始化配置。 从类对象中加载。env参数确定在什么环境下的
app.config.from_object(envs.get(env)) # 加载初始化扩展库。通过懒加载的方式加载(调用函数时参数的传递)
init_ext(app) # 加载初始化api路由器。通过懒加载的方式加载(调用函数时参数的传递)
init_api(app) return app
  • views.py
 from Book.route import books_api

 def init_api(app):
books_api.init_app(app)
  • books/route.py
 from flask_restful import Api

 from Book.views import BooksResource

 books_api = Api()

 books_api.add_resource(BooksResource, "/books/")
  • books/views.py
 from flask_restful import Resource

 class BooksResource(Resource):

     def get(self):
return {"msg": "book ok"}


 

flask 之(六) --- API|RestfulApi的更多相关文章

  1. flask开发restful api系列(8)-再谈项目结构

    上一章,我们讲到,怎么用蓝图建造一个好的项目,今天我们继续深入.上一章中,我们所有的接口都写在view.py中,如果几十个,还稍微好管理一点,假如上百个,上千个,怎么找?所有接口堆在一起就显得杂乱无章 ...

  2. flask开发restful api系列(7)-蓝图与项目结构

    如果有几个原因可以让你爱上flask这个极其灵活的库,我想蓝图绝对应该算上一个,部署蓝图以后,你会发现整个程序结构非常清晰,模块之间相互不影响.蓝图对restful api的最明显效果就是版本控制:而 ...

  3. flask开发restful api

    flask开发restful api 如果有几个原因可以让你爱上flask这个极其灵活的库,我想蓝图绝对应该算上一个,部署蓝图以后,你会发现整个程序结构非常清晰,模块之间相互不影响.蓝图对restfu ...

  4. Python+Flask搭建mock api server

    Python+Flask搭建mock api server 前言: 近期由于工作需要,需要一个Mock Server调用接口直接返回API结果: 假如可以先通过接口文档的定义,自己模拟出服务器返回结果 ...

  5. Flask(六)—— 自定义session

    Flask(六)—— 自定义session import uuid import json from flask.sessions import SessionInterface from flask ...

  6. Flask之RESTFul API前后端分离

    Flask之RESTFul API前后端分离 一:虚拟环境搭建的两种方式 1 pipenv的使用 pip install --user pipenv安装pipenv在用户目录下 py -m site ...

  7. Python使用Flask实现RESTful API,使用Postman工具、requests库测试接口

    RESTful是一种API设计规范.在RESTful架构中,主要使用POST,DELETE,PUT和GET四种HTTP请求方式分别对指定的URL资源进行增删改查操作. RESTful之前的做法: /u ...

  8. 使用 Python 和 Flask 设计 RESTful API

    近些年来 REST (REpresentational State Transfer) 已经变成了 web services 和 web APIs 的标配. 在本文中我将向你展示如何简单地使用 Pyt ...

  9. flask开发restful api系列(6)-配置文件

    任何一个好的程序,配置文件必不可少,而且非常重要.配置文件里存储了连接数据库,redis的用户密码,不允许有任何闪失.要有灵活性,用户可以自己配置:生产环境和开发环境要分开,最好能简单的修改一个东西, ...

随机推荐

  1. SSM框架中使用日志框架

    在 pom,xml 配置 Log4j jar 添加一个 mybatis_log.xml 文件 完整配置信息 <?xml version="1.0" encoding=&quo ...

  2. jQuery和vue 设置ajax全局请求

    一个很常见的问题,如果用户登录网站session过期,需要用户返回登录页面重新登录. 如果是http请求可以在后台设置拦截器,统一拦截并跳转.但是ajax方法并不能通过后台直接跳转. 所以我们可以写一 ...

  3. 部署flask到阿里云服务器ECS

    比较难的一点是:部署到云服务器上之后,通过公网没法访问. 这就要说回道 本地开发时的一个小细节:通过http://127.0.0.1:5000是可以访问的,但通过http://[本地ip]:5000是 ...

  4. 24.stark组件全部

    admin组件: 博客里面的图片的是在太难弄了,有大哥会弄给我贴一片博客,我一个一个加太累了,没有加 admin参考:https://www.cnblogs.com/yuanchenqi/articl ...

  5. 小程序开发之后台SSM环境搭建(一)

    1.新建web项目 打开eclipse,选择file-->New-->Dynamic web Project ,填写项目名字,一直点击next,勾选Generate web.xml dep ...

  6. Sublime Text 3 全程详细图文使用教程

    一. 前言 使用Sublime Text 也有几个年头了,版本也从2升级到3了,但犹如寒天饮冰水,冷暖尽自知.最初也是不知道从何下手,满世界地查找资料,但能查阅到的资料,苦于它们的零碎.片面,不够系统 ...

  7. Codeforces 1214 F G H 补题记录

    翻开以前打的 #583,水平不够场上只过了五题.最近来补一下题,来记录我sb的调试过程. 估计我这个水平现场也过不了,因为前面的题已经zz调了好久-- F:就是给你环上一些点,两两配对求距离最小值. ...

  8. jQuery的replaceWith()函数用法详解

    replaceWith,替换元素 replaceWith() 方法将选择的元素的内容替换为其他内容. 我们先在先看一个实例 <!DOCTYPE html> <html> < ...

  9. 微信小程序_(校园视)开发上传视频业务

    微信小程序_(校园视) 开发用户注册登陆 传送门 微信小程序_(校园视) 开发上传视频业务 传送门 微信小程序_(校园视) 开发视频的展示页-上 传送门 微信小程序_(校园视) 开发视频的展示页-下 ...

  10. JavaWeb-SpringBoot_(上)腾讯云点播服务之视频的上传-demo

    使用Gradle编译项目 传送门 腾讯视频云点播 传送门 项目已托管到Github上 传送门 腾讯云点播服务之视频的显示(下) 传送门 个人腾讯云控制台中的视频管理 IndexController.j ...