Python Flask 框架 终极部署教程,超详细。Uwsgi+Nginx+mysql+Supervisor+Virtualenv, 基于阿里云默认Linux

我发现网上还没完整详细版本的Flask 部署教程,而我在部署中遇到很多坑,所以在这里写下来,完整的教程以下是部署流程:

处理本地的代码

假设你已经完成了项目的开发,本地已经安装了git,那么首先将你的代码提交到git;

#进项目根目录
pip freeze > requirements.txt  #导flask 全部包,方便新环境下一次性安装。
git init # 之前如果没有做,那么需要做
git add --all #提交所有修改后的文件
git remote add origin http:xxxx.git  #这一步如果之前没做,那么你需要做。
git commmit -w 'first commit'

安装Mysql

现在进入服务器正式开始搭建环境,首先安装mysql.

新鲜出炉的阿里云需要更新下apt.
apt-get update
然后安装mysql, 注意一定记住root 密码,还要在配置文件中设置字符为UTF8!
增加新的数据库,注意名字和你项目的一样,
create database xxx;

安装虚拟环境 Vitualenv

pip install virtualenvwrapper #直接安装虚拟环境容器来顺便安装virtualenv
注意需要先修改环境变量。否则命名无法使用
vim ~/.bashrc   在最底部增加
#这是为了让 WORKON 命令能用。
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

然后你需要用 mkvirtualenv xxx   来创建一个叫xxx 的python 虚拟环境
workon xxx #切换到你的虚拟环境
在这个环境里,你可以升级你的python 到python3, 当然根据你的需求。

安装 Git 拿到全部源码

现在在服务器上安装git 拿到源码
apt install git
git init
git remote add origin http:xxx.git
git pull origin master # 拿到全部源码

正式部署源码

进去项目根目录:
pip install -r requirements.txt  #导入你项目的flask 依赖
pip manager.py db migerate #初始化数据库  
# 这里如果出现初始化失败,那么清空你的 migration 文件夹,你会丢掉你的测试数据,当然有不丢的办法
pip manager.py db upgrade #导入数据库,代码迁移完成

安装Nginx

apt install nginx #安装nginx 
贴下配置文件位于: /etc/nginx/conf.d/xxx.conf
# 配置nginx与uwsgi的通信方式和名称,mysite就是名称
upstream xxx {
    # nginx使用socket的方式与uwsgi进行通信
    # 这里指向你项目的目录下的socket文件,
    # 这个socket文件不需要单独创建,在运行的时候会自动创建。
    server unix:///srv/xxx/xxx.sock;
}
# 配置服务器
server {
    # 监听的端口号
    listen 80;
    # 域名或者本机的ip地址
    server_name dev.wudizu.com 47.104.22.138;
    charset     utf-8;
    # 最大的上传大小
    client_max_body_size 75M;  
    # adjust to taste

    # 访问根目录下的任何url的配置
    location / {
        # 指定访问哪个upstream
        uwsgi_pass wudi;
        # 包括uwsgi参数的文件
        include     /etc/nginx/uwsgi_params;
    }
    location /static {
        alias /srv/xxx/static;
   }

}

安装Uwsgi

pip install uwsgi

贴下配置文件:


[uwsgi]
chdir           = /srv/xxx
# 模块
module          = firstweb   #注意这里一定要你写的flask 首页文件名
# python的虚拟环境
home            = /root/.virtualenvs/flask-py2

# 是否启用mater模式
master          = true

# 进程数
processes       = 10

# socket文件地址

socket          = /srv/xxx/xxx.sock

# wsgi文件

wsgi-file       = /srv/xxx/xxx.ini

# wsgi文件中的app变量

callable        = app

# socket文件的权限

chmod-socket    = 666

安装Supervisor

sudo pip install supervisor
# supervisor的程序名字
[program:mysite]
# supervisor执行的命令
command=uwsgi --ini mysite_uwsgi.ini
# 项目的目录
directory = /srv/xxx 
# 开始的时候等待多少秒
startsecs=0
# 停止的时候等待多少秒
stopwaitsecs=0# 自动开始
autostart=true
# 程序挂了后自动重启
autorestart=true
# 输出的log文件
stdout_logfile=/srv/xxx/log/supervisord.log
# 输出的错误文件
stderr_logfile=/srv/xxx/log/supervisord.err

[supervisord]
# log的级别
loglevel=info

# 使用supervisorctl的配置
[supervisorctl]
# 使用supervisorctl登录的地址和端口号
serverurl = http://127.0.0.1:9001

# 登录supervisorctl的用户名和密码
username = admin
password = 123

[inet_http_server]
# supervisor的服务器
port = :9001
# 用户名和密码
username = admin
password = 123

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface


然后使用supervisor运行uwsgi:
supervisord -c supervisor.conf

使用supervisorctl管理supervisord:supervisor是一个C/S模型,跟redis一样,有一个服务器,也有一个客户端命令用来管理服务的,使用以下命令进入到命令界面:
supervisorctl -c supervisor.conf

指定的文件必须和supervisord服务端保持一致。 一些常用的命令有:
supervisorctl -c supervisor.conf
> status    # 查看状态
> start program_name # 启动程序
> restart program_name # 重新启动程序
> stop program_name # 停止程序
> reload # 重新加载配置文件
> quit # 退出当前的客户端

使用flask来做一个小应用

提示:博主默认你已经具备了Python的基础知识,已经能够很顺畅的编写一些Python脚本,否则接下来你会比较难看懂。

旧版

这里先给出旧版本的一些使用截图,初始化的时候的样子

未分类

模糊查询

未分类

精确查询

未分类

技术

这个应用比较简单,所使用的技术也比较少,主要有以下技术要点

  • requests模拟请求
  • 正则匹配关键字
  • web.py搭建web环境
  • vue.js做数据自动绑定

是不是很简单?

Flask Web开发基于Python的Web应用开发实战pdf http://www.gooln.com/document/283768.html

在这个小应用中使用web.py的时候目录结构是这样的

未分类

其中static目录里面存放的是静态资源

未分类

结构相当简单

python代码

这里给出全部的Python代码

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

import requests
import json
import web
import sys
import re

reload(sys)
sys.setdefaultencoding('utf8')

urls = (
    "/", "index",
    "/query", "Query"
)


render = web.template.render('static', cache=False)


class index:
    def GET(self):
        return render.index('static')


class Query:
    def POST(self):
        keywords = str(web.input().get('shopname'))
        url_base = "https://list.tmall.com/search_product.htm?q="+keywords
        headers = {"User-Agent": "iphone7"}

        try:
            result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('n', '').replace(' ','')
            infostr = re.findall(r'j_shop_moreshop_more">(.+?)
', result_base) shoplist = [] for item in infostr: scorelist = re.findall(r'">(.+?)<iclass="', item) thisShopname = re.findall(r'(.+?)', item)[0] shoplist.append('{"shopname": "'+ thisShopname +'" , "dsr": "'+scorelist[0]+'", "service": "'+scorelist[1].split('">')[1]+'","ship": "'+scorelist[2].split('">')[1]+'"}') return json.dumps({"code": 0, "rows":list(set(shoplist))}) except Exception, e: print e return json.dumps({"code": -1, "msg": "没查询到相关店铺"}) if __name__ == "__main__": app = web.application(urls, globals()) app.run()

前端HTML代码

$def with (urlbase)

<html lang="zh-CN">

        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">



        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">

店铺:{{ item.shopname }}



描述相符:{{ item.dsr }}
服务态度:{{ item.service }}
物流服务:{{ item.ship }}
<script type="text/javascript" src="$urlbase/jquery.min.js"> <script type="text/javascript" src="$urlbase/vue.js"> <script type="text/javascript" src="$urlbase/index.js">

js代码

var mainVM = new Vue({
    el: 'body',
    data: {
        shopes:[
            {
                shopname:'未查询',
                dsr:'未查询',
                service:'未查询',
                ship:'未查询'
            }
        ]
    },
    methods:{
        query:function(){
            var _self = this,keyword = $('input[name="shopname"]').val();
            $.post('/query',{"shopname":keyword},function (data) {
                if(data.code == 0){
                    _self.shopes = [];

                    for(var k in data.rows){
                        var thisdata = JSON.parse(data.rows[k]);
                        _self.shopes.push({
                            shopname:thisdata.shopname,
                            dsr:thisdata.dsr,
                            service:thisdata.service,
                            ship:thisdata.ship
                        })
                    }
                }else{
                    alert('查询出错,错误信息:'+data.msg);
                }
            },"json");
        }
    }
});

可以说代码部分也是相当简单,前端HTML和js的代码就不解释了,很容易看懂,这里只对app.py做简单的解释。

观察天猫的搜索页面,发现天猫pc端跟手机端页面都可以轻松抓取,但是使用手机端页面会更加快速方便,因为结构上更加清晰,而且数据量少,抓取速度更快

如何实现只抓取手机端页面的数据呢?很简单,这里我们只需要定义以下HTTP的请求头信息就可以了,也就是headers,如下定义

headers={“User-Agent”:”iphone7″}

  
天猫的搜索链接是使用的get请求,地址为

”https://list.tmall.com/search_product.htm?q=”+keywords

参数只需要传入一个关键字就可以了,前端使用ajax把数据POST给服务端,服务端接收使用下面的这句话

keywords=str(web.input().get(‘shopname’))

是不是马上就搞定了关键的几步了?接下来发起请求拿到数据就可以了

result_base=requests.get(url=url_base,headers=headers,timeout=15).content.replace(‘n’,”).replace(”,”)

注意,这里我把返回的结果中的换行跟空格都去掉了,因为我这里所需要的数据很简单,为了匹配方便我直接给替换成可空,也就是后面的这个

.replace(‘n’,”).replace(”,”)

然后根据正则匹配的字符串进行遍历组合成结果返回给前端就好了,前端直接使用vue.js进行数据的绑定,几乎不需要DOM操作就可以完成结果列表的渲染,棒!(这里强行安利一波vue.js)

前后端通信使用json进行数据交互,友好而且方便。

重写

上面给出了所需要的技术要点和关键代码,那么现在我需要使用flask重写一遍,当然了,关键部分还是不用变动,只是处理方式上稍微有些差异,如果会用web.py,那么使用flask上手应该是很快的。

1、web.py的处理方式

在使用web.py的时候我们启动一个web服务很简单,通常执行以下命令

python app.py

这样我们就启动了一个web服务,但是这样的话会有很多问题,主要有以下几点

  • 不能关闭终端窗口,否则应用结束,一般用于调试
  • 多个应用的时候公用Python环境会引起冲突

注意:

web.py并不适合高并发的应用,但是作为一般应用还是可以轻松应对的。

以上命令执行后web.py会在8080端口绑定一个web服务,如果你想创建多个应用,那么你应该在后面加上端口号

如果你使用了多个域名指向一台机器的多个应用,那么你应该使用nginx来转发请求,而不是直接输入域名加端口号

在远程vps上运行开发完成的应用时,你可以执行以下命令把web以后台服务的形式运行

nohup python app.py 

这种方式简单粗暴,但是仅仅作为临时方案是可行的,运行上述命令后你可以安心的关掉终端,而且web服务依然在运行,但是一旦重启了服务器,那么就得重新登录vps再次执行命令,不是很方便。

2、flask的处理方式

flask和web.py类似,它自带了一个web服务器,默认绑定在5000端口,但是它本身自带的web服务器并不是很好,安全性也不高,作为开发使用还是足够的,正式生产环境中不太建议直接使用flask自带的web服务。

好了,现在可以开始了,为了解决上面提到几个问题,这里咱们来使用一个新东西,上面说了多应用环境冲突的问题,在这儿可以使用一个叫做“虚拟环境”的东西解决。

“虚拟环境”就是直接复制一个Python的全局环境,但是是独立出来的,你可以在这个环境里面安装各种模块,而且不会影响到Python的全局环境,也就是说如果你把其中的一个“虚拟环境”给玩坏了,起不来了,那么你只需要删掉坏的“虚拟环境”重新创建一个就可以了,这些操作都不会对Python全局环境有任何的影响,安全又方便,下面咱们就来创建一个“虚拟环境”。

博主使用的开发环境是Ubuntu 16.04 并没有自带这个软件,使用下面的命令安装

sudo apt-get install python-virtualenv -y

安装完之后测试下是否安装成功

~$ virtualenv --version
15.0.1

接下来咱们创建一个叫 tmall 虚拟环境用于运行我们的应用

~$ virtualenv tmall
Running virtualenv with interpreter /usr/bin/python2
New python executable in /home/kbdancer/tmall/bin/python2
Also creating executable in /home/kbdancer/tmall/bin/python
Installing setuptools, pkg_resources, pip, wheel...done.

创建的时候会给出创建的位置,如果你需要在指定的目录下面创建虚拟环境,那么你得切换到目标目录,然后执行创建命令,博主这里直接在自己的用户目录下面执行的创建命令,自然就是在用户目录下面生成的一个 tmall 文件夹,文件夹下面自动生成了Python环境

未分类

安装完之后需要将这个环境激活才能使用,执行下面的命令进行激活

~$ source tmall/bin/activate
(tmall) :~$

接着在虚拟环境中安装flask环境(博主默认你的Python全局环境中已经有了easy_install或者pip),博主这里使用pip进行安装

~$ pip install flask

好了,所需要的环境配置完成,接下来就可以开始写小应用了。

3、开始编码

编码这个环节应该是快速而且高效的,上面我们已经给出了旧代码,关键部分直接复制过来就能用,稍微改改就可以跑起来了。

flask默认使用Jinja2作为模板引擎,Jinja2在进行模板渲染的时候通常会识别{{}}中的内容进行填充,但是这里博主遇到了一个尴尬的问题,Vue.js也是使用的{{}}作为标识符进行渲染,这就导致了冲突,访问页面的时候就会出现如图所示的错误

未分类

当然,解决方法还是有的,参考这篇文章进行配置 解决Jinja2与Vue.js的模板冲突 解决思路也比较简单,就是在需要Jinja2渲染的时候添加一个空格,而vue.js渲染的时候则不需要空格,python脚本如下

from flask import Flask, render_template

app = Flask(__name__)
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'

前端HTML代码修改后就成了这样

<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Hello world</title>
    </head>
    <body>
        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">
            <p>店铺:{{item.shopname}}</p>
            <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p>
        </div>
        <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
    </body>
</html>

Jinja2默认会在templates目录下面寻找模板文件,而静态文件比如css,js之类的默认存储在static目录下面,这里我们按照Jinja2的默认设置稍微进行修改,当然,如果你想自定义模板目录或者静态文件的目录也是可以的,只需要稍微的配置下就行了,博主这里按照默认的规则来设置。

很快,我们的小应用就跑起来了

未分类

这里还是需要提到几个关键点:

flask中接收前端传递过来的参数用到的是request对象,前端使用json把数据post到后端,后端使用下面这句进行接收

request.form.get('shopname')

更多详细使用方法参考这个地址 http://www.letiantian.me/2014-06-24-flask-process-post-data/ 接着测试下小应用能不能正常运行

未分类

未分类

OK,测试通过。

4、关于部署

由于这个小应用比较简单,部署起来可以按照常规的部署方式进行,但是并不适合生产环境,所以这里暂时不写如何部署,下次有大型网站案例的时候再详细写如何部署以及优化。

5、完整代码

python部分

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

from flask import Flask, render_template, request
import requests
import json
import re

app = Flask(__name__)
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/query', methods=['POST'])
def query():
    keywords = request.form.get('shopname')
    url_base = "https://list.tmall.com/search_product.htm?q=" + keywords
    headers = {"User-Agent": "iphone7"}

    try:
        result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('n', '').replace(' ', '')
        infostr = re.findall(r'j_shop_moreshop_more">(.+?)
', result_base) shoplist = [] for item in infostr: scorelist = re.findall(r'">(.+?)<iclass="', item) thisShopname = re.findall(r'(.+?)', item)[0] shoplist.append('{"shopname": "' + thisShopname + '" , "dsr": "' + scorelist[0] + '", "service": "' + scorelist[1].split('">')[1] + '","ship": "' + scorelist[2].split('">')[1] + '"}') return json.dumps({"code": 0, "rows": list(set(shoplist))}) except Exception, e: print e return json.dumps({"code": -1, "msg": "没查询到相关店铺"}) if __name__ == "__main__": app.run(debug=True)

HTML部分

<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Hello world</title>
    </head>
    <body>
        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">
            <p>店铺:{{item.shopname}}</p>
            <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p>
        </div>
        <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
    </body>
</html>

JS部分

没有做任何改动,就不贴出来了

总结

写这篇文章的目的一来是复习下flask的一些知识,二来是与web.py做个对比,再者就是给入门的朋友提供一个实战的例子,方便参考。

Flask 中模块化应用的实现

Flask是一个轻量级的Web框架。虽然是轻量级的,但是对于组件一个大型的、模块化应用也是能够实现的,“蓝图”就是这样一种实现。对于模块化应用的实现,在Flask 0.2版本中进行了设计。本文暂时不对“蓝图”做详细的介绍,而是先从0.2版本中的Module类的实现讲起。其实,“蓝图”的实现和Module类的实现很相似。

为什么实现模块化应用

对于大型应用而言,随着功能的不断增加,整个应用的规模也会扩大。按照一定的规则将应用的不同部分进行模块化,不仅能够使整个应用逻辑清晰,也易于维护。例如,在Flask中,你也许想像如下构建一个简单的项目:

/myapplication
    /__init__.py
    /views
        /__init__.py
        /admin.py
        /frontend.py

以上目录结构中,我们将之前的Flask单文件修改成了一个应用包,所有的视图函数都在views下,并且按照功能分为了admin和frontend两个部分。为了实现这种模块化应用的构建,在0.2版本中Flask实现了Module类。这个类实例可以通过注册的方式,在Flask应用创建后添加进应用。

Module类实现了一系列的方法:

  • route(rule, **options)

  • add_url_rule(rule, endpoint, view_func=None, **options)

  • before_request(f)

  • before_app_request(f)

  • after_request(f)

  • after_app_request(f)

  • context_processor(f)

  • app_context_processor(f)

  • _record(func)

以上方法除了add_url_rule和_record外,都可以作为装饰器在自己的模块中使用,这些装饰器都返回一个函数。通过调用_record方法,可以将装饰器返回的函数放到_register_events中。当Flask应用创建之后,通过运行_register_events列表中的函数,可以将这个模块注册到应用中去。

Flask应用怎么注册一个Module

以下我们以一个例子来说明Flask应用怎么注册一个Module。

1. 项目结构

这个简单的例子项目结构如下:

/myapplication
    /__init__.py
    /app.py
    /views
        /__init__.py
        /admin.py
        /blog.py

admin.py和blog.py两个模块的代码如下:

# admin.py
from flask import Module

admin = Module(__name__)

@admin.route('/')
def index():
    return "This is admin page!"

@admin.route('/profile')
def profile():
    return "This is profile page."
# blog.py
from flask import Module
blog = Module(__name__)
@blog.route('/')
def index():
    return "This is my blog!"
@blog.route('/article/<int:id>')
def article(id):
    return "The article id is %d." % id

以上两个模块中,我们首先分别创建了一个Module类,然后像写一般的视图函数一样,为每个模块增加一些规则。之后,可以在创建Flask应用的时候将这些模块引入,就可以注册了。

# app.py
from flask import Flask
from views.admin import admin
from views.blog import blog
app = Flask(__name__)
@app.route('/')
def index():
    return "This is my app."
app.register_module(blog, url_prefix='/blog')
app.register_module(admin, url_prefix='/admin')
if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 5000, app)

在app.py中:

  • 我们首先引入了admin和blog两个Module对象;

  • 之后,我们创建了一个Flask应用app,并且为这个应用增加了一个视图函数;

  • 为了注册模块,我们调用了应用的register_module方法;

  • 最后,从werkzeug.serving中我们调用run_simple方法,用来创建一个本地的服务器用于测试这个Flask应用。

根据以上的步骤,我们就可以测试这个应用。分别以/blog和/admin为URL前缀,就可以访问blog和admin两个模块了。

2. 注册Module时发生了什么

根据上面的例子,只要简单的调用Flask应用的register_module方法,就可以注册一个Module了。关于register_module方法的代码如下:

def register_module(self, module, **options):
    """Registers a module with this application.  The keyword argument
    of this function are the same as the ones for the constructor of the
    :class:`Module` class and will override the values of the module if
    provided.
    """
    options.setdefault('url_prefix', module.url_prefix)
    state = _ModuleSetupState(self, **options)
    for func in module._register_events:
        func(state)

通过以上代码可以发现:

  • 可以通过增加url_prefix来区分不同的Module,这在app注册admin和blog时我们已经看到了;

  • 在注册时,我们创建了一个_ModuleSetupState的类,这个类接收Flask应用和一些参数生成一个state实例。这个实例反映了当前Flask应用的状态。

  • 前面在讲到Module类的时候,我们讲到Module未注册时会将自己模块的一些功能实现都放在_register_events列表中,这些功能实现都是函数形式。当需要将模块注册到某一应用上时,只需要传递关于这个应用信息的参数即可,即就是上面的state实例。这样,通过运行函数,可以讲一些属性绑定到当前应用上去。

以上面例子中不同模块的URL绑定来讲,通过注册,应用app现形成了如下的URL“地图”:

>>> app.url_map
Map([<Rule '/admin/profile' (HEAD, GET) -> admin.profile>,
     <Rule '/admin/' (HEAD, GET) -> admin.index>,
     <Rule '/blog/' (HEAD, GET) -> blog.index>,
     <Rule '/' (HEAD, GET) -> index>,
     <Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>,
     <Rule '/static/<filename>' (HEAD, GET) -> static>]
    )
>>> app.url_map._rules_by_endpoint
{'admin.index': [<Rule '/admin/' (HEAD, GET) -> admin.index>],
'admin.profile': [<Rule '/admin/profile' (HEAD, GET) -> admin.profile>],
'blog.article': [<Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>],
'blog.index': [<Rule '/blog/' (HEAD, GET) -> blog.index>],
'index': [<Rule '/' (HEAD, GET) -> index>],
'static': [<Rule '/static/<filename>' (HEAD, GET) -> static>]
}
>>> app.view_functions
{'admin.index': <function views.admin.index>,
'admin.profile': <function views.admin.profile>,
'blog.article': <function views.blog.article>,
'blog.index': <function views.blog.index>,
'index': <function __main__.index>
}

这样,就可以把不同模块的URL规则放在一起,并在endpoint和视图函数之间形成对应关系。

python利用flask,gunicorn部署接口全过程

背景

无论开发 Android 还是 Web 端程序,有时候不免需要一些接口供自己调用进行测试,这里记录一下详细的过程。

环境配置

1、阿里云的 9.9 的学生特惠机, Ubuntu 14.04 64位

未分类

2、利用 putty 输入公网 ip 登录到服务器

先配置编码为 utf-8

未分类

未分类

输入账号密码

未分类

3、要利用 apt-get 包管理工具下载各种包,所以先更新下源,随手把 git 也安一下,安装中途输入 Y 即可

apt-get update
apt-get install git

未分类

Flask [flɑ:sk]

Flask是一个使用Python编写的轻量级Web应用框架。基于Werkzeug WSGI工具箱和Jinja2 模板引擎。 Flask 官方教程: http://docs.jinkan.org/docs/flask/index.html

我已经在本地写了一个简单的 Flask ,并且传到了 Coding

#coding=utf-8
from flask import Flask,jsonify,request
app = Flask(__name__) #获得 Flask 实例
 #写一个 POST 的方法,传一个 name 的参数,返回一个  json 数据
@app.route('/getJson', methods=['POST'])
def getJson():
    #bala bala,下边随便返回点东西
    name=request.form.get('name', default='windliang')
    data={
    'name':name,
    'place':'wuhan'
    }
    msg='get success'
    code=True
    return getBaseReturnValue(data,msg,code)
#返回 json 数据    
def getBaseReturnValue(data,msg,code):
  json_data = jsonify({'data':data,'msg':msg,'success':code})
  return json_data

if __name__ == '__main__':
    app.run(host='0.0.0.0') #在服务器上运行

依次执行下边的指令,‘//’后边的仅做注释不用复制

git clone https://git.coding.net/wind_liang/flasktest.git  //将 coding 上的代码克隆到本地
pip install virtualenv  //python 的一个创建隔离环境的工具
mkdir api  //创建一个新目录 api,virtualenv 直接用 clone 下的目录会出问题,原因不清楚
cp -i flasktest/api.py api  //将 coding 下载下来的 api.py 复制到新目录 api 中
cd api   // 进入 api 目录
virtualenv venv // 创建虚拟环境安包的文件夹
source venv/bin/activate //激活虚拟环境 ,这时会看到在最前边有 (venv) 标识 ,以后进入前都得先激活环境
pip install flask // 安装 flask 包
pip freeze > requirements.txt  // 将安装的包列表写到 reqirements.txt 中,
以后每次 pip 安装了新的库的时候,都需freeze 一次,
既能知道自己安装了什么库,也方便别人部署时,安装相应的库。
python api.py  // 运行程序 
ctrl+C 用于终止当前 python 程序
deactivate 用于关闭当前虚拟环境 ,先不用执行此句

virtualenv 是一个将不同项目所需求的依赖分别放在独立的地方的一个工具,它给这些工程创建虚拟的Python环境。它解决了“项目X依赖于版本1.x,而项目Y需要项目4.x”的两难问题,而且使你的全局site-packages目录保持干净和可管理。官方教程

阿里云还得设定开放 5000端口

未分类

未分类

此时利用 postman (谷歌浏览器的一个插件),或者 curl 等其他能发 post 请求的工具进行测试

未分类

未分类

ctrl+C 终止程序,进入下一步

gunicorn

Gunicorn是一个Python WSGI UNIX的HTTP服务器。这是一个预先叉工人模式,从Ruby的独角兽(Unicorn)项目移植。该Gunicorn服务器与各种Web框架兼容,我们只要简单配置执行,轻量级的资源消耗,以及相当迅速。现在我们使用 flask 自带的服务器,完成了 web 服务的启动。生产环境下,flask 自带的服务器,无法满足性能要求。我们这里采用 gunicorn 做 wsgi容器,用来部署 python

pip install gunicorn  //安装 gunicorn
pip freeze > requirements.txt //保存到当前安装的包的列表中
gunicorn -w4 -b0.0.0.0:8000 api:app
此时,我们需要用 8000 的端口进行访问,
原先的5000并没有启用。
其中 gunicorn 的部署中,
-w 表示开启多少个 worker,-b
表示 gunicorn  的访问地址 ,
api 是程序进入的文件名, 
app 是代码中获得的 flask 实例
进入阿里云记得开放8000端口

未分类

未分类

ctrl + c 终止当前程序

微信公众号服务器端 Flask 源码

python 2.7

# -*- coding: utf-8 -*-
# filename: main.py
from flask import Flask
import hashlib
import time
from flask import Flask,g,request,make_response
import xml.etree.ElementTree as ET
import requests 
import re
import os
from bs4 import BeautifulSoup
import warnings
app = Flask(__name__)
BASE_DIR = os.path.dirname(__file__) #获取当前文件夹的绝对路径
warnings.filterwarnings("ignore") #忽略警告
@app.route("/wx",methods=["GET","POST"])
def wx():
    if request.method == "GET":       # 判断请求方式是GET请求
        my_signature = request.args.get('signature')     # 获取携带的signature参数
        my_timestamp = request.args.get('timestamp')     # 获取携带的timestamp参数
        my_nonce = request.args.get('nonce')        # 获取携带的nonce参数
        my_echostr = request.args.get('echostr')         # 获取携带的echostr参数
        token = 'helloworld'     # 一定要跟微信端填写的token一致
        # 进行字典排序
        data = [token,my_timestamp ,my_nonce ]
        data.sort()
        # 拼接成字符串
        try:
            temp = ''.join(data)
        except:
            return "success"
        # 进行sha1加密
        mysignature = hashlib.sha1(temp).hexdigest()
        # 加密后的字符串可与signature对比,标识该请求来源于微信
        if my_signature == mysignature:
            return my_echostr
    else:
        rec = request.stream.read()
        xml_rec = ET.fromstring(rec)
        msgType=xml_rec.find("MsgType").text 
        if msgType != 'text': #只对文字进行回复
            return "success"
        tou = xml_rec.find('ToUserName').text
        fromu = xml_rec.find('FromUserName').text
        content = xml_rec.find('Content').text
        cc=content.encode('UTF-8') # 用户发送的文字
        t="狼吃羊"
        if(cc==t):
            content="http://windliang.oschina.io/worfeatsheep/"
        else:
            return "success"
        xml_rep = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content><FuncFlag>0</FuncFlag></xml>"
        response = make_response(xml_rep % (fromu,tou,str(int(time.time())), content))
        response.content_type='application/xml'
        return response

@app.route("/",methods=["GET"])
def index():
    return "hello,world"

if __name__ == '__main__':
    app.run(host='0.0.0.0',debug='true')

使用Docker配置Flask开发环境

使用Docker+docker-compose,配置最简Flask开发环境

工具

  • Docker
    一种开源容器应用,供开发者打包自己的开发环境,可以任意移植

  • docker-compose
    一种管理多个Docker容器的工具,可以简化我们启动容器的各种命令

配置文件

首先我们需要一个python基础景象,Docker各种基础镜像都可以从官方找到 https://hub.docker.com/_/python/ 。找到基础镜像之后就可以基于它做相应的配置,这些操作都记录在Dockerfile中。

Dockerfile:

FROM python:3.6-slim # 官网中挑选的python基础镜像

ADD requirements.txt requirements.txt # requirements.txt罗列了需要安装的python模块,将文件复制到容器中

RUN pip install -r ./requirements.txt # 执行模块安装

EXPOSE 5000 # 对外暴露5000端口

requirements.txt

Flask # 需要安装的python模块,如有其他需要安装的模块,如下依次写入。
# redis 
# pymongo

配置完Dockerfile之后,开始配置docker-compose文件。

docker-compose.yml

web:
  build: ../../dockerfile/python/3 # DockerFile所在目录
  ports:
    - "5000:5000" # 对外暴露端口,与Dockerfile中端口号一致
  volumes:
    - ~/workspace/python/redis:/code # 本地工作目录与容器中目录映射
  command:
    - /code/app.py # 处理请求的python脚本

app.py

#!/usr/bin/env python

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Flask Dockerized'

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0')

容器启动

在docker-compose.yml所在文件夹执行命令 docker-compose up ,在控制台中看到如下输出:

Starting flash_web_1 ...
Starting flash_web_1 ... done
Attaching to flash_web_1
web_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web_1  |  * Restarting with stat
web_1  |  * Debugger is active!
web_1  |  * Debugger PIN: 166-703-177

同时在浏览器中输入 localhost:5000 ,并正常显示文本 Flask Dockerized ,环境就配置成功啦!对应的log都可以在启动容器的终端中查看到。例如:

web_1  | 172.17.0.1 - - [13/Sep/2017 13:18:17] "GET / HTTP/1.1" 200 -
web_1  | 172.17.0.1 - - [13/Sep/2017 13:18:17] "GET /favicon.ico HTTP/1.1" 404 -

总结

以上配置了最简的Flask开发环境,实际开发中还需要数据库、缓存、nginx等,这些基础容器都可以在Docker官网中找到,并使用docker-compose可以很清晰的将这些容器关联起来。

基于Flask实现后台权限管理系统 – Flask

一、Flask

1、简介

Flask是使用Python编写的轻量级Web应用框架,其WSGI工具箱采用Werkzeug,模板引擎则使用Jinja2。Flask使用BSD授权,Flask也被称为“microframework”,因为它使用简单的核心,用extension增加其他功能,用户可以随意地进行组合,满足自己的要求。

一段最简单的Flask代码如下:

from flask import Flask

app = Flask(__name__)



@app.route('/')

def hello_world():

    return 'Hello World!'



if __name__ == '__main__':

    app.run()

那么,这段代码做了什么?

首先,我们导入了Flask类。这个类的实例将会是我们的WSGI应用程序。

接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),你应该使用name,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 ‘main‘ 或实际的导入名)。这是必须的,这样 Flask才知道到哪去找模板、静态文件等等。详情见Flask的文档。

然后,我们使用route()装饰器告诉Flask什么样的URL能触发我们的函数。

这个函数的名字也在生成URL时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。

最后我们用run()函数来让应用运行在本地服务器上。 其中if name == ‘main‘: 确保服务器只会在该脚本被Python解释器直接执行的时候才会运行,而不是作为模块导入的时候。

2、Flask-SQLAlchemy

Flask-SQLAlchemy是对Flask的一个插件,是Flask对SQLAlchemy的封装,SQLAlchemy提供非常强大ORM(Object Relation Model 对象关系模型)功能。

常见情况下对于只有一个Flask应用,所需要做的事情就是创建Flask应用,选择加载配置,接着创建SQLAlchemy对象时候,把Flask应用传递给它作为参数。

一旦创建,这个对象就包含 sqlalchemy 和 sqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类:

class User(db.Model):

  id = db.Column(db.Integer, primary_key=True)

  username = db.Column(db.String(80), unique=True)



  def __init__(self, username, email):

    self.username = username



  def __repr__(self):

    return'<User %r>' % self.username

2.1 一对多关系

以资源类型和资源举例说明,它们之间关系是一对多关系,一个资源类型下可以有多个资源,而资源只属于一种类型,如果前面介绍,资源只能是菜单类型或功能类型的一种。

class ResourceType(db.Model):

  __tablename__ = 'SYRESOURCETYPE'

  ID = db.Column(db.String(36), primary_key=True)

  CREATEDATETIME = db.Column(db.DateTime, index=True, default=datetime.now)

  UPDATEDATETIME = db.Column(db.DateTime, index=True, default=datetime.now)

  NAME = db.Column(db.String(100))

  DESCRIPTION = db.Column(db.String(200))

  resources = db.relationship('Resource', backref='type', lazy='dynamic')
class Resource(db.Model):

    __tablename__ = 'SYRESOURCE'

    ID = db.Column(db.String(36), primary_key=True)

    # 省略部分内容

    NAME = db.Column(db.String(100))

    SYRESOURCETYPE_ID = db.Column(db.String, db.ForeignKey('SYRESOURCETYPE.ID'))

ResourceType类通过relationship关联到Resource类,给自己定义了resources集合属性,代表本类型下的所有资源。db.relationship关联到Resource类,通过backref动态地给Resource类增加了type属性,代表Resource所属的资源类型。

2.2 多对多关系

以用户和角色为例,它们之间的关系是多对多关系,一个用户可以有多个角色,一个角色也可以被多个用户所用。

user_role_table = db.Table(‘SYUSER_SYROLE’, db.Model.metadata

, db.Column(‘SYUSER_ID’, db.String, db.ForeignKey(‘SYUSER.ID’))

, db.Column(‘SYROLE_ID’, db.String, db.ForeignKey(‘SYROLE.ID’)))

class User(db.Model):

    __tablename__ = 'SYUSER'

    ID = db.Column(db.String(36), primary_key=True)

    LOGINNAME = db.Column(db.String(100), unique=True, index=True)

    PWD = db.Column(db.String(100))

    NAME = db.Column(db.String(100))



    roles = db.relationship('Role',secondary=user_role_table,

    backref=db.backref('users', lazy='dynamic'))
class Role(db.Model):

    __tablename__ = 'SYROLE'

    ID = db.Column(db.Integer, primary_key=True)

    NAME = db.Column(db.String(100))

User类中通过db.relationship定义了和Role类之间的关系,它们之间连接的桥梁就是user_role_table,user_role_table定义了两张关联表之间主键的对应关系。User对象含有roles集合属性,代表角色的集合,而通过backref动态地给Role对象增加了users属性,代表本角色所从属的用户有哪些。

注:ORM将对象映射到数据表,对象之间的关系映射到表之间的关系,通过操作对象及其之间的关系即可以操作数据库表,非常方便,不需要写SQL语句。

3、集成Python Shell

Flask-Script的shell命令可以导入指定的对象,这样就可以在控制台操作Python对象,譬如用户对象、资源对象等等,即时测试对象的属性和方便,使用起来比较方便。

###########################manager.py####################

from app.models import User, Role, Resource, ResourceType, Organization

from flask_script import Manager, Shell

from flask_migrate import Migrate, MigrateCommand



def make_shell_context():

    return dict(app=app, db=db, User=User, Role=Role, Resource=Resource,

ResourceType=ResourceType, Organization=Organization)



manager.add_command("shell", Shell(make_context=make_shell_context))

manager.add_command('db', MigrateCommand)



@manager.command

def myprint():

    print 'hello world'

代码使用flask_script下的Manager模块,通过add_command方法将一些对象注册到shell上下文中。注册的方法有两种:

  • add_command。直接调用Manager模块下的add_command方法;
> python manager.py shell

>>> db

<SQLAlchemy engine='mysql://root:@127.0.0.1/authbase?charset=utf8'>

>>> app

<Flask 'app'>

>>> User.query.all()

[<User u'u8d85u7ea7u7ba1u7406u5458'>

, <User u'u7ba1u7406u5458'>

]

>>>
  • 装饰器方式。使用manager.command装饰器,装饰的方法名称就会成为shell环境中的一条命令。
python manager.py myprint

hello world

4、蓝图

蓝图实现了应用的模块化,使用蓝图使得应用层次更加清楚,开发者可以更容易地开发和维护项目。蓝图通常作用于相同的URL前缀,譬如/admin/user, /admin/resource等等,将一类相同的功能组合到一起,如果URL前缀发生变化,只需要修改蓝图即可。

举例说明,通常网站分为用户网站部分和管理后台部分,如博客类网站,管理后台可以编辑文章等操作,编辑后的文章会在用户网站部分显示,其它用户可以阅读、评论等,同时,管理后台可以评论进行管理等等。按照模块划分:

Blog -------|

| 前台网站

  ------------|app/blog/:id

  ------------|app/blog/:id/comment/:id

| 管理后台

         |admin/user/:id

         |admin/role/:id

                 |admin/siteinfo

上述Blog将网站分为两个部分:前台网站和管理后台,如果按照蓝图实现,即将应用划分为两个蓝图。

from flask import Blueprint

app  = Blueprint('app', __name__, url_prefix='/app')

admin = Blueprint('admin', __name__, url_prefix='/admin')



@app.route('/blog/:id', methods=['POST'])

defapp_blog():

    #查询指定编号博客赋值给blog对象

    returnrender_template(blog/index.html', blog=blog)



@admin.route('/role/:id', methods=['POST'])

defadmin_role():

     #查询指定编号角色赋值给role对象

    return render_template(role/index.html', role=role)

Flask连接mysql数据库初级到进阶过程

未分类

前言

在初学 Flask 的时候,在数据库连接这部分也跟每个初学者一样。但是随着工作中项目接手的多了,代码写的多了,历练的多了也就有了自己的经验和技巧。在对这块儿代码不断的进行升级改造后,整理了自己在连接数据库这部分的的一个学习经验,也就是我们今天分享的连接数据库部分的打怪升级之旅。希望可以为大家在学习 Python 的路上提供一些参考。

初级阶段

首先安装 Mysql 扩展包

未分类

建立数据库链接

未分类

开启打怪升级之路

在日常开发中,连接数据库最多的应用场景就是,查询所有数据和查询单条数据。就以查询所有数据场景为例。

小白版本——在后端凭接表格,传到前端渲染

未分类

进阶阶段

第一关——后端消灭 HTML 标签

后端:

未分类

前端:

未分类

第二关——让返回值更优雅

未分类

第三关——让代码更简洁

未分类

一个更高效的方式——直接将返回的嵌套元祖转换为嵌套的字典,常用与只查询 ID, Username 的场景

未分类

经验总结

作为一个程序员学习新的技术知识都是必须的,我们都是自己事业上无人可替的开拓者,我们都是要经历从入门到熟练再到精略的过程,过程虽然很痛苦不过收获的喜悦也是别人羡慕不来的,IT 大牛 不是那么容易就练成的。希望今天的分享能够帮助到大家。

Ubuntu安装配置uWSGI Nginx部署Flask项目

关于 uWSGI ,可以先看这篇文章。简单来说,WSGI 是一个 Python 协议,定义了应用程序(我们写的软件)如何与 Web 服务器(如 Nginx)通信,WSGI 只是一个接口。而 uWSGI 是一个支持多种语言的服务器容器,使用 WSGI 定义的标准实现与多种 Web 服务器的通信,并将 Web 服务器发来的请求“翻译”成应用程序所能理解形式。

安装

Python 2:

sudo apt-get update
sudo apt-get install python-pip python-dev nginx

Python 3:

sudo apt-get update
sudo apt-get install python3-pip python3-dev nginx

安装 Flask 和 uwsgi:

pip install uwsgi flask

创建一个简单的 Flask 项目 ~/myproject/myproject.py:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

创建启动文件为 ~/myproject/run.py:

from myproject import app

if __name__ == "__main__":
    app.run()

运行 python run.py ,然后访问 http://server_domain_or_IP:5000 将会看到:

未分类

当然直接使用 python run.py 的方法只适合本地开发,线上的话速度太慢,我们需要使用 uwsgi。

uwsgi

首先确保你安装了 uwsgi,然后运行:

uwsgi --socket 0.0.0.0:5000 --protocol=http -w run:app

protocol 说明使用 http 协议,-w 指明了要启动的模块,run 就是项目启动文件 run.py 去掉扩展名,app 是 run.py 文件中的变量 app,即 Falsk 实例。然后访问 http://server_domain_or_IP:5000,同样会看到上图。说明 uwsgi 可以正常运行。

但是这样的话每次都从命令行启动太麻烦,可以在 ~/myproject/目录下创建一个配置文件 myproject.ini:

[uwsgi]
module = run:app
master = true
processes = 3

chdir = /home/ubuntu/myproject
socket = /path/to/sock/myproject.sock
logto = /home/to/log/ishuhui.log
chmod-socket = 660
vacuum = true
  • processes = 5 说明要启动5个子进程处理请求;module = run:app 和命令行使用的意义一样;
  • chdir = /home/ubuntu/myproject 只想我们项目的根目录,即 run.py 所在文件夹;
  • socket = /path/to/sock/myproject.sock 是 uwsgi 启动后所需要创建的文件,这个文件用来和 Nginx 通信,后面会在配置 Nginx 时用到,所以 chmod-socket = 660 是为了修改 .sock 文件权限来和 Nginx 通信;
  • logto = /home/to/log/ishuhui.log 指明了 uwsgi 日志目录,uwsgi 会将请求历史写入该文件。

配置完成后运行:

uwsgi --ini myproject.ini

可以看到 /path/to/sock/myproject.sock 目录下多了 myproject.sock 文件,用来和 Nginx 通信。接下来配置 Nginx。

配置 Nginx

配置 Nginx 特别简单,找到 Nginx 配置文件(sudo vim /etc/nginx/sites-available/default),修改为如下格式:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/path/to/sock/myproject.sock;
    }
}

其实重要的就两行:

include uwsgi_params;
uwsgi_pass unix:/path/to/sock/myproject.sock;

接下来不出意外,访问 80 端口,就可以看到你的程序了。

将 uwsgi 设置为系统服务

我们运行 uwsgi –ini myproject.ini 之后,按 ctrl+c 或者关闭 ssh 连接窗口,都会导致 uwsgi 进程关闭。 uwsgi 进程一关闭,.sock 文件就会消失,这时访问网站 Nginx 就会报错:

未分类

这时,我们需要进程管理软件管理 uwsgi 进程的运行了。Ubuntu 自带的 systemd 是最简单的方法,可以将我们的项目变为系统服务。首先创建 myproject.service 文件 sudo vim /etc/systemd/system/myproject.service:

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=lufficc
Group=www-data
WorkingDirectory=/home/ubuntu/myproject
Environment=FLASKR_SETTINGS=/home/ubuntu/myproject/env.cfg
ExecStart=/usr/local/bin/uwsgi --ini /home/ubuntu/myprojectenv/ishuhui.ini

[Install]
WantedBy=multi-user.target
  • WorkingDirectory: 你的项目目录。
  • Environment:需要的环境变量,比如指明你的项目的配置文件。
  • ExecStart:服务启动的代码。
  • WantedBy=multi-user.target:指明会跟随系统启动而启动该服务。

注意以上所有路径为绝对路径。

接下来可以愉快的启动了(myproject 就是 myproject.service 文件名去掉扩展名):

sudo systemctl start myproject
sudo systemctl restart myproject
sudo systemctl stop myproject

python flask-sqlalchemy如何设置使自动建的mysql表字符集charset为utf8

问题

发现flask-sqlalchemy自动创建的mysql表为默认的latin1,如何不更改mysql服务器的默认字符集,直接在flask里配置自动建的mysql表字符集为utf8?

最佳答案

配置table_args就可以了,如:

class Foo(Base):
    __tablename__ = "foo"
    __table_args__ = {'mysql_collate': 'utf8_general_ci'}

    ...

    column = db.Column(db.String(500))

python flask before_request、after_request修饰器截获所有请求

在学习着用flask开发安卓后天接口时,遇到一个需求,就是想截获所有请求,即在所有请求进入app.route装饰的函数前先被处理一次。

经过在网上查找资料后,知道了@before_request、@after_request这两个方法,示例:

@app.before_request
def before_request():
    ip = request.remote_addr
    url = request.url
    print ip,
    print url

before_request()函数被app.before_request修饰以后,每一次请求到来后,都会先进入函数before_request()中,如上代码,获取请求的ip以及url,并打印出来,执行完毕后请求才会正常进入到app.route修饰的函数中响应,如果有多个函数被app.before_request修饰了,那么这些函数会被依次执行。

app.before_request修饰器在开发中用处非常大,比如判断某个ip是否有恶意访问行为,从而进行拦截等操作。

此外同理,app.after_request修饰器是在用户请求得到函数响应后被执行,不过需要注意的是这个执行是在函数返回数据前被调用,即请求已经被app.route修饰的函数响应过了,已经形成了response,但还未返回给用户的时候,调用的。