暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Flask + Ant Vue 前端后分离开发实战(2)-【flask新手】入门基础

小儿来一壶枸杞酒泡茶 2021-02-23
1050

前言

万丈高楼平地起,本节主要是简单的对我们的Flask做一些基础的知识铺垫。

1 涉及的知识点

  • flask 本地开发环境搭建和简单示例演示
  • flask 路由URL
  • flask 请求报文和响应报文,用Debug查看具体参数值信息
  • flask 钩子函数
  • flask 蓝图和蓝图钩子

2 新手基础入门

Flask和bottle一样是一个微型的python web开发框架。它仅仅提供核心的web框架所需的一些实现,如:WSGI和Werkzeoug和Jinja2模板引擎(后端渲染模式下使用视图模式展示页面),其他实现需要通过扩展插件来实现。

2.1 本地开发环境搭建和简单示例演示

通常我们的开发环境是都是在本地进行开发,开完完成后再由CI/CD(持续集成/持续交付,持续部署)进行相关线上生产环境的部署。

至于到线上部署的时候,通常我们会基于pipevn工作流方式来安装部署我们的环境。目前暂时不需要。

2.1.1 环境说明

  • IDE 使用pycharm
  • window 10

2.1.2 环境搭建

默认情况,你的本地windows已经是安装好了相关python环境,如果没有的话,建议尝试自己安装一下。

(1)通过pycharm创建项目

创建成功后,开始在我们的虚拟环境里面安装相关依赖开发包。

(2)安装相关依赖

File-->setting--->

修改我们的pip安装源,避免需要翻墙安装依赖包的痛苦!点击上图的 ‘+’

修改我们的安装源为:https://mirrors.aliyun.com/pypi/simple/

开始安装我们的flask框架和想看的开发依赖包。

安装成功后会提示相关的信息:

2.1.3 启动一个接口服务测试

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(host='127.0.0.1',port='8888',debug=True)

右键,run

或者在我们的编辑器下面命令行执行相关操作:

(AntFlask) D:\code\python\local_python\AntFlask>python main.py
 * Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 104-589-570
 * Running on http://127.0.0.1:8888/ (Press CTRL+C to quit)


接下来我们可以通过浏览器或具体的访问接口的工具访问我们的请求接口地址:

上述的代码主要是我们的创建了一个Flask对象,然后使用这个对象启动一个服务,就是启动一个web服务!

2.2 路由URL

项目是基于前端后分类方式开发,所以后端主要做的任务其实就像提供前端所需的相关API接口。那对应的API接口就是我们的定义的路由URL,通过访问路由URL获取指定的相关数据信息。

一个web请求相关流程是:

  1. 浏览器访问请求地址如:http://127.0.0.1:8888
  2. Flask接收到浏览器发出的请求后进行url解析。
  3. url解析到指定对应的我们的编写的函数位置内部进行相关逻辑处理。
  4. 逻辑处理完成后返回相关响应报文信息给浏览器。
  5. 浏览器接收响应报文信息并进行解析和展示相关内容。

那所谓的路由就是指@app.route("/"),这里我们默认使用/来定义我们的URL的访问规则。被@app.route("/")进行枣庄市的函数就是我们的需要处理业务逻辑的视图函数。

被@app.route装饰的这个过程,其实就是给注册路由一个过程。

新增一个函数视图,返回我们的所有注册的路由

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"


@app.route("/index")
def index():
    return "Hello Index!"

@app.route("/info")
def info():
    return "Hello Info!"

@app.route('/all_rule_urls')
def route_map():
    """
    主视图,返回所有视图网址
    "
""
    rules_iterator = app.url_map.iter_rules()
    return jsonify(
        {rule.endpoint: rule.rule for rule in rules_iterator if rule.endpoint not in ('route_map''static')})

if __name__ == "__main__":

    app.run(host='127.0.0.1',port='8888',debug=True)

访问:http://127.0.0.1:8888/all_rule_urls

路由多URL绑定:

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"


@app.route("/index")
def index():
    return "Hello Index!"

@app.route("/info")
def info():
    return "Hello Info!"

@app.route('/all_rule_urls')
@app.route('/get_rule_urls')
@app.route('/rules_urls')
def route_map():
    """
    主视图,返回所有视图网址
    "
""
    rules_iterator = app.url_map.iter_rules()
    return jsonify(
        {rule.endpoint: rule.rule for rule in rules_iterator if rule.endpoint not in ('route_map''static')})

if __name__ == "__main__":

    app.run(host='127.0.0.1',port='8888',debug=True)

动态路由:

动态路由的意思就是,我们的路由是不固定,可能是由具体的参数值来决定。

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"


@app.route("/index")
def index():
    return "Hello Index!"

@app.route("/info")
def info():
    return "Hello Info!"

@app.route('/all_rule_urls')
@app.route('/get_rule_urls')
@app.route('/rules_urls')
def route_map():
    """
    主视图,返回所有视图网址
    "
""
    rules_iterator = app.url_map.iter_rules()
    return jsonify(
        {rule.endpoint: rule.rule for rule in rules_iterator if rule.endpoint not in ('route_map''static')})

@app.route('/hi/<name>/')
@app.route('/hi/<name>')
def route_hi(name):
    return "你好:" +name

if __name__ == "__main__":

    app.run(host='127.0.0.1',port='8888',debug=True)

访问:http://127.0.0.1:8888/hi/xiaozhong 或 http://127.0.0.1:8888/hi/xiaozhong/

注意事项点:我们的视图函数在同一个模块下,不能存在同命名,因为URL映射规则是根据它的命名来生成

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"


@app.route("/index")
def index():
    return "Hello Index!"

@app.route("/info")
def info():
    return "Hello Info!"

@app.route('/all_rule_urls')
@app.route('/get_rule_urls')
@app.route('/rules_urls')
def route_map():
    """
    主视图,返回所有视图网址
    "
""
    rules_iterator = app.url_map.iter_rules()
    return jsonify(
        {rule.endpoint: rule.rule for rule in rules_iterator if rule.endpoint not in ('route_map''static')})

@app.route('/hi/<name>/')
@app.route('/hi/<name>')
def route_hi(name):
    return "你好:" +name



if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

可以通过 print(app.url_map) 查看具体的URL的规则Rule:

Map([<Rule '/get_rule_urls' (OPTIONS, HEAD, GET) -> route_map>,
 <Rule '/all_rule_urls' (OPTIONS, HEAD, GET) -> route_map>,
 <Rule '/rules_urls' (OPTIONS, HEAD, GET) -> route_map>,
 <Rule '/index' (OPTIONS, HEAD, GET) -> index>,
 <Rule '/info' (OPTIONS, HEAD, GET) -> info>,
 <Rule '/' (OPTIONS, HEAD, GET) -> hello>,
 <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>,
 <Rule '/hi/<name>' (OPTIONS, HEAD, GET) -> route_hi>,
 <Rule '/hi/<name>/' (OPTIONS, HEAD, GET) -> route_hi>])
 * Debugger is active!
 * Debugger PIN: 104-589-570
 * Running on http://127.0.0.1:8888/ (Press CTRL+C to quit)

指定路由访问的提交方法:

示例:

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request
app = Flask(__name__)

# 允许使用Get 和POST方法提交到此接口
@app.route("/h1",methods=["GET","POST"])
def hello():
    print(request)
    return "Hello World!"

# 只允许使用Get方法提交到此接口
@app.route("/h2",methods=["GET"])
def hello_2():
    print(request)
    return "Hello World!"


# 只允许使用PUT方法提交到此接口
@app.route("/h3",methods=["PUT"])
def hello_3():
    print(request)
    return "Hello World!"





if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

2.3 Http请求报文和响应报文,用Debug查看具体参数值信息

2.3.1 什么是请求报文和响应报文:

从浏览器里查看:请求报文三个主体信息:

响应报文主体信息:

使用Debug来查看我们的请求报文信息:

给程序打断点,用于调试查看我们的具体请求报文信息:

右键,Debug run

访问路由地址127.0.0.1:8888/

在对应的断点的地方进程查看:

查看更多详细的参数信息:

我们关注的Flask中request的请求对象

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request
app = Flask(__name__)

@app.route("/")
def hello():
    print(request)
    return "Hello World!"

if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

这里面有很多信息,但是不需要过多去了解太多,只提取关键的信息即可。比如request 对象里面包含了每次请求进来的是包含相关的参数和提交请求方式信息即可。

2.3.2 请求参数获取

get 方法下参数提取
#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request,Blueprint
app = Flask(__name__)

@app.route("/index", methods=["GET"])
def index():
    name = request.args.get("name")
    age = request.args.get("age")
    return "hello name=%s, age=%s" % (name, age)

if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

POST 方法下参数提取
#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request,Blueprint
app = Flask(__name__)

@app.route("/index", methods=["POST"])
def index():
    name = request.form.get("name")
    age = request.form.get("age")
    return "hello name=%s, age=%s" % (name, age)

if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

2.4 Flask请求钩子Hook

钩子函数的意思就是,一个请求来之前和请求完成之后我们可以执行什么事情。

请求钩子处理流程:

  1. 接收请求
  2. 创建请求上下文
  3. 请求上下文入栈
  4. 创建该请求的应用上下文
  5. 应用上下文入栈
  6. 处理逻辑
  7. 请求上下文出栈
  8. 应用上下文

flask中的场景几个钩子函数:

  • before_first_request:在处理app第一个请求前运行。

  • before_request:在每次请求前运行。

  • after_request:如果处理逻辑没有异常抛出,在每次请求后运行。

  • teardown_request:在每次请求后运行,即使处理发生了错误。

  • teardown_appcontext:在应用上下文从栈中弹出之前运行

钩子函数的示例说明:

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request
app = Flask(__name__)


# before_request装饰的方法会加载到app的before_request_funcs列表中,按加载的顺序依次执行,不需要参数
@app.before_request
def rest_test():
    print('每次请求进来的时候都会执行的函(请求来临之前预处理)''--2')
    pass

# before_first_request装饰的函数加载到before_first_request_funcs列表中,只不过在app第一次接收到请求后执行,其他时候不再执行
@app.before_first_request
def app_first_request():
    print('应用启动后第一个请求进来的时候执行(只执行一次)' + '--1')

# after_request装饰的函数加载到after_request_funcs列表中,传入的参数是response对象,可以对其进行拦截修改,必须返回一个response对象
@app.after_request
def after_request(resopon):
    print(resopon)
    print('每次请求返回响应体信息后,我们的可以处理的事情--3')
    return resopon

# teardown_request装饰的函数加载到teardown_request_funcs中,如果发生了异常则传入error的对象,无异常参数为None,无返回值
@app.teardown_request
def teardown_request(error):
    print('请求异常发生的时候!')
    print(error)
    print('--4')

# teardown_appcontext装饰的函数加载到teardown_appcontext_funcs中,如果发生了异常则传入error的对象,无异常参数为None,无返回值
@app.teardown_appcontext
def teardown_appcontext(error):
    print('--5')


# 允许使用Get 和POST方法提交到此接口
@app.route("/h1",methods=["GET","POST"])
def hello():
    print(request)
    return "Hello World!"


if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

启动程序访问:http://127.0.0.1:8888/h1

第一次访问:
应用启动后第一个请求进来的时候执行(只执行一次)--1
每次请求进来的时候都会执行的函(请求来临之前预处理)--2
<Request 'http://127.0.0.1:8888/h1' [GET]>
<Response 12 bytes [200 OK]>
每次请求返回响应体信息后,我们的可以处理的事情--3
请求异常发生的时候!
None
--4
--5
第二次访问:
127.0.0.1 - - [23/Feb/2021 11:43:48] "GET /h1 HTTP/1.1" 200 -
127.0.0.1 - - [23/Feb/2021 11:44:24] "GET /h1 HTTP/1.1每次请求进来的时候都会执行的函(请求来临之前预处理)--2
<Request 'http://127.0.0.1:8888/h1' [GET]>
"
 200 -
<Response 12 bytes [200 OK]>
每次请求返回响应体信息后,我们的可以处理的事情--3
请求异常发生的时候!
None
--4
--5

注意事项点:

在debug模式下,teardown_request和teardown_appcontext装饰的函数不会执行

after_request请求钩子会自动传入response对象作为参数,同时必须返回一个response对象

before_request装饰的函数不需要返回数据,如果返回了数据,那么视图函数不会再执行,而是直接返回结果

钩子函数后续的作用:

  1. 定义我们的自己中间件处理

  2. 处理日志记录

2.5 Flask蓝图

大白话说法就是我们的API进行分组,统一规划我们的API请求的前缀。比如我们的后台有:系统模块,系统模块有用户模块,有权限模块,有角色模块等。

2.5.1 蓝图应用示例

然后下面再对应不同的分组:

  • 比如/sys/info--系统组
  • 比如/user/info--用户组

示例代码:

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask, jsonify, request, Blueprint

app = Flask(__name__)

sysblue = Blueprint('sys', __name__, url_prefix='/sys')


@sysblue.route('/info')
def index():
    return 'OK'


userblue = Blueprint('user', __name__, url_prefix='/user')


@userblue.route('/info')
def index():
    return 'OK'


app.register_blueprint(sysblue)
app.register_blueprint(userblue)

if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1', port='8888', debug=True)


启动服务后打印的信息如下:

Map([<Rule '/user/info' (HEAD, GET, OPTIONS) -> user.index>,
 <Rule '/sys/info' (HEAD, GET, OPTIONS) -> sys.index>,
 <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
 * Debugger is active!
 * Debugger PIN: 104-589-570
 * Running on http://127.0.0.1:8888/ (Press CTRL+C to quit)

说明我们已经可以使用蓝图进行分组了!

2.5.2 蓝图的钩子(可以扩展为特定分组下加上特定的中间件的处理)

#!/usr/bin/evn python
# coding=utf-8

from flask import Flask,jsonify,request,Blueprint
app = Flask(__name__)


sysblue = Blueprint('sys', __name__, url_prefix='/sys')

# 蓝图也可以为主app添加请求钩子,before_app_first_request装饰会在app的before_first_request_funcs列表中,以None为键;
@sysblue.before_app_first_request
def app_first_request():
    print('first_request' + '--1')

# 加载到app的before_request_funcs列表中,在None蓝图下,按加载的顺序依次执行,不需要参数
@sysblue.before_app_request
def app_request():
    pass

# 加载到app的before_request_funcs列表中,在testblue蓝图下
@sysblue.before_request
def blue_before_request():
    pass

# 加载到after_request_funcs列表中,在testblue蓝图下
@sysblue.after_request
def after_request(rsp):
    return rsp

# 加载到after_request_funcs列表中,在None蓝图下
@sysblue.after_app_request
def blue_after_app_request(rsp):
    return rsp

#teardown_request装饰的函数加载到app的teardown_request_funcs中,在testblue蓝图下
@sysblue.teardown_request
def teardown_request(error):
    print(error)
    print('--4')

# 和teardown_request功能一样,在None蓝图下
@sysblue.teardown_app_request
def blue_teardown_app_request(error):
    pass

@sysblue.route('/index')
def index():
    return 'OK'

app.register_blueprint(sysblue)

if __name__ == "__main__":
    print(app.url_map)
    app.run(host='127.0.0.1',port='8888',debug=True)

参考资料来源

[1] 请求响应和http协议 https://www.cnblogs.com/yzy-kyk/p/12608194.html

[2] HTTP协议详解 https://blog.51cto.com/6638225/1859503

[3] 钩子函数 https://www.cnblogs.com/cwp-bg/p/10094912.html

END

后续会继续抽时间把之前做的东西做相关的整理和输出!

小钟同学 | 文  【原创】【转载请联系本人】| QQ:308711822


文章转载自小儿来一壶枸杞酒泡茶,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论