Flask-1-03-HelloWorld
Flask程序运行过程:
所有Flask程序必须有一个程序实例
Flask调用视图函数后,会将视图函数的返回值作为响应的内容,返回给客户端。一般情况下,响应内容主要是字符串和状态码。
当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。此时,Web服务器使用WSGI(Web Server Gateway Interface)协议,把来自客户端的所有请求都交给Flask程序实例。WSGI是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求、解析HTTP请求、发送HTTP,响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。
程序实例使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。根据每个URL请求,找到具体的视图函数。 在Flask程序中,路由的实现一般是通过程序实例的route装饰器实现。route装饰器内部会调用add_url_route()方法实现路由注册。
调用视图函数,获取响应数据后,把数据传入HTML模板文件中,模板引擎负责渲染响应数据,然后由Flask返回响应数据给浏览器,最后浏览器处理返回的结果显示给客户端。
了解一门框架从HelloWorld开始
新建文件helloworld.py:
# 导入Flask类 from flask import Flask #Flask类接收一个参数__name__ app = Flask(__name__) # 装饰器的作用是将路由映射到视图函数index @app.route('/') def index(): return 'Hello World' # Flask应用程序实例的run方法启动WEB服务器 if __name__ == '__main__': app.run()
这些就已经足够了,那么我们来运行一下helloworld.py(到保存helloworld.py的路径下):
$ cd /home/python/code
$ python helloworld.py
能够看出服务器已经启动,地址是127.0.0.1 端口:5000,这时候去浏览器访问查看结果
初始化参数:
- import_name: 模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录
- static_url_path:访问静态资源的url前缀, 默认值是static
- static_folder: 默认‘static’ 静态文件的目录
- template_folder: 默认‘templates’ 模板文件的目录
... app = Flask(__name__, static_url_path="/python", # 访问静态资源的url前缀, 默认值是static static_folder="static", # 静态文件的目录,默认就是static template_folder="templates", # 模板文件的目录,默认是templates ) ...
配置参数
配置参数默认有3中方式:
- app.config.from_pyfile(“config.cfg”) 从文件导入
-
app.config.from_object() 从对象中导入
- app.config.from_envvar() 从环境变量中导入(应该没有人愿意用)
1. app.config.from_pyfile(“config.cfg”) :在项目的根目录下创建一个config.cfg文件
1 from flask import Flask 2 3 app = Flask(__name__, 4 static_url_path="/python", # 访问静态资源的url前缀, 默认值是static 5 static_folder="static", # 静态文件的目录,默认就是static 6 template_folder="templates", # 模板文件的目录,默认是templates 7 ) 8 9 # 配置参数的使用方式 10 # 1. 使用配置文件 11 app.config.from_pyfile("config.cfg") 12 13 14 @app.route("/") 15 def index(): 16 """定义的视图函数""" 17 return "helloworld" 18 19 20 if __name__ == '__main__': 21 # 启动flask程序 22 app.run()
# config.cfg 文件 DEBUG = True
目录结构
这样你重新运行一下
2.app.config.from_object() 从对象中导入
创建一个类,来定义类属性为配置项
# coding:utf-8 from flask import Flask app = Flask(__name__, static_url_path="/python", # 访问静态资源的url前缀, 默认值是static static_folder="static", # 静态文件的目录,默认就是static template_folder="templates", # 模板文件的目录,默认是templates ) # 2. 使用对象配置参数 class Config(object): DEBUG = True # 使用方法加载设置的配置 app.config.from_object(Config) @app.route("/") def index(): """定义的视图函数""" return "hello world" if __name__ == '__main__': # 启动flask程序 app.run()
3.直接操作config对象
from flask import Flask app = Flask(__name__, static_url_path="/python", # 访问静态资源的url前缀, 默认值是static static_folder="static", # 静态文件的目录,默认就是static template_folder="templates", # 模板文件的目录,默认是templates ) # 3. 直接操作config的字典对象 app.config["DEBUG"] = True @app.route("/") def index(): return "hello flask" if __name__ == '__main__': # 启动flask程序 app.run()
读取配置参数
1.如果你在当前能访问到app的情况下
app.config.get("DEBUG")
2.如果你无法拿到app这个对象时,你只需要导入current_app,也是可以拿到配置信息的
current_app.config.get("DEBUG")
from flask import Flask, current_app @app.route("/") def index(): """定义的视图函数""" # 读取配置参数 # 1. 直接从全局对象app的config字典中取值 # print(app.config.get("ITCAST")) # 2. 通过current_app获取参数 # print(current_app.config.get("ITCAST")) return "hello flask"
app.run 参数
if __name__ == '__main__': # 启动flask程序 # app.run() app.run(host="0.0.0.0", port=5000, debug=True) # port指定的端口,debug 是唯一可以指定在这里的配置项
你在开发的状态下,如果局域网中不需要别的主机访问,你可以不指定(host),如果你想在同一局域网中,别的主机也可以访问到,并且你自己还想以回环地址(127.0.0.1)访问的话就可以指定为‘0.0.0.0’
扩展: 0.0.0.0
IPV4中,0.0.0.0地址被用于表示一个无效的,未知的或者不可用的目标。
* 在服务器中,0.0.0.0指的是本机上的所有IPV4地址,如果一个主机有两个IP地址,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。
* 在路由中,0.0.0.0表示的是默认路由,即当路由表中没有找到完全匹配的路由的时候所对应的路由。用途总结:
- 当一台主机还没有被分配一个IP地址的时候,用于表示主机本身。(DHCP分配IP地址的时候)
- 用作默认路由,表示”任意IPV4主机”。
- 用作服务端,表示本机上的任意IPV4地址。
现在页面基本是可以展示了,定义url的规则我们是通过装饰器来添加 [ @app.route() ],那么如果我有很多的页面,我想直观的查看一下具体都定义了哪些,我们这里就可以通过:app.url_map 来查看
from flask import Flask, current_app app = Flask(__name__, static_url_path="/python", # 访问静态资源的url前缀, 默认值是static static_folder="static", # 静态文件的目录,默认就是static template_folder="templates", # 模板文件的目录,默认是templates ) class Config(object): DEBUG = True app.config.from_object(Config) @app.route("/") def index(): """定义的视图函数""" return "hello flask" if __name__ == '__main__': # 启动flask程序 print(app.url_map) # 打印路由的详情 app.run()
返回结果
(Flask_py) python@python-VirtualBox:~/code$ python hello.py # 首先'/' 是我们index定义的路由规则 # (HEAD, OPTIONS, GET)可以通过元组中这三种方式访问 # index是指定的视图函数 Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, # /python/<filename> 这是访问静态页面的路由规则 # (HEAD, OPTIONS, GET)默认的访问方式 # static 静态资源 <Rule '/python/<filename>' (HEAD, OPTIONS, GET) -> static>]) * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat
这里我们也可以通过访问方式来做一些限制性的功能:
比如指定一个视图,只能通过post方式请求
这里我们定义一个视图 指定访问方式为POST
...
@app.route('/post_only', methods=['POST']) def post_only(): return 'post method page'
...
返回结果:这里可以看出爆了一个405的状态码。DEBUG给我们爆出的错误是GET这种请求方式是不允许的
这里我们将GET请求方式也添加到列表中,查看一下是否可以访问
...
@app.route('/post_only', methods=['GET','post']) def post_only(): return 'post method page'
...
通过返回的结果可以看出,已经可以访问,并且返回了我们的响应信息
通过请求方式,我们可以的到一些限制,那么我们如果定义同样的路由规则,它又会有怎样的功能呢?
首先以相同的路径相同的访问方式我们来查看一下会产生什么样的结果
# 这里我们有两个视图分别为hello1、hello2 @app.route('/hello') def hello1(): return 'This is a hello 1' @app.route('/hello') def hello2(): return 'This is a hello 2'
返回结果:页面展示我们可以看出hello2这个视图函数并没有执行,但是通过【app.url_map】我们可以看出两个视图都在集合当中。
<Rule '/hello' (HEAD, OPTIONS, GET) -> hello1>, <Rule '/hello' (HEAD, OPTIONS, GET) -> hello2>,
很明显,它们两个路径规则和访问方式,都是相同的,所以当匹配到第一个的时候,就不会在往下执行,那么接下来,我们给hello1指定POST请求方式,hello2指定GET请求方式,会返回怎样的结果呢?
# 这里我们分别给hello1、hello2设置了请求方式 @app.route('/hello', methods=['POST']) def hello1(): return 'This is a hello 1' @app.route('/hello', methods=['GET']) def hello2(): return 'This is a hello 2'
返回结果:
虽然访问的路径是相同的,但是请求方式的不同,也是可以帮我们限制想要访问的视图
这里两个函数用了一个路径,反之一个函数使用两个路径那么也应该是可以的
# 定义了一个视图为test @app.route('/test1') @app.route('/test2') def test(): return 'two routes are used for one page'
通过返回结果可以看出,返回的是同一个页面
在路径当中重定向是非常常见的事情,这里我们也简单的介绍一下
这里就用到redirect()
# 导入redirect来实现页面重定向 from flask import Flask, current_app, redirect ... @app.route("/") def index(): """定义的视图函数""" return "index page" # 创建一个login视图,当执行完毕后直接返回到index页面 @app.route('/login') def login(): # 定义index视图的路径 url = '/' return redirect(url) ...
返回结果:访问127.0.0.1:5000/login页面
这里有一个隐患在里面,如果有一天,我们认为index视图的路径 '/' 不太适合了,想更改为'/index',那么项目里所有跳转到index的页面就需要全部改变,这样就会给我们程序带来隐患,那么我们找到一个可以反向解析的方法,只要这个视图函数还在,不管改变什么路径,我们后续跳转页面都不需要更改,那么就需要使用 url_for('需要跳转的函数名')
# 优化代码,使用反向解析来实现跳转到 index 页面 from flask import Flask, current_app, redirect, url_for ... @app.route("/") # 当这里路径更改之后,下面login视图中的地址也不需要改变 def index(): return "index page" @app.route('/login') def login(): url = url_for('index') return redirect(url) ...
返回结果:
之前我们一直都是用写死的方式,来定义路由规则,这肯定是不能满足我们的开发需求:
如何动态的设置一个路由规则呢?
转换器:Flask默认给我们指定了3种,使用方法如下:
int | 接受整数 |
float | 接收浮点数 |
path | 和默认的相同,但也接受斜线 |
# 这里指定int,尖括号中冒号后面的内容是动态的 @app.route('/user/<int:id>') def user(id): return '欢迎id: %d的用户' %id
这里如果不指定类型,路由传递的参数默认当做string处理
# 127.0.0.1:5000/user/hannibal @app.route('/user/<username>') def user(username): return '欢迎 %s ' %username
返回结果:
如果这里爆出:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128). 点击这里
即使是拥有了int、float、string 这些也是不能满足我们的需求,那么我们就需要自定义转换器,大体的流程是,我们需要定义一个类来自定义我们的转换器,这个类我们可以随便起名,这里就用RegexConverter(非常重要的一点是这个类需要继承werkzeug.routing中的BaseConverter这个类),接下来需要将自定义的转换器添加到Flask的应用中。最后就是函数来使用这个自定义的转换器,举个栗子:
from flask import Flask, current_app, redirect, url_for from werkzeug.routing import BaseConverter # 我们需要继承的转换器类 app = Flask(__name__) class RegexConverter(BaseConverter): """自定义转换器类""" def __init__(self, url_map, regex): # 调用父类的初始化方法 这里我们需要将url_map传给父类 super(RegexConverter, self).__init__(url_map) # 将正则表达式的参数保存到对象的属性中,flask会去使用这个属性来进行路由的正则匹配 self.regex = regex # 这个就是我们规定的正则匹配的规则 r'1[34578]\d{9}' self.regex是固定的写法 # 将自定义的转换器添加到flask的应用中
# 这里我们向converters这个字典中添加了一个re键对应我们的转换器,re自己可以随便命名 app.url_map.converters['re'] = RegexConverter # 这里模拟匹配手机号码 # 127.0.0.1:5000/send/138XXXXXXXX @app.route("/send/<re(r'1[34578]\d{9}'):phone_num>") def send_sms(phone_num): return "send message to %s" % phone_num if __name__ == '__main__': # 查看路由信息 print(app.url_map) # 启动flask程序 app.run(debug=True)
返回结果:这里如果你填写一个不匹配的手机号,就一定会报错,那么说明你自定义的转换器就生效了
这里你也可以重新创建一个视图比如匹配邮箱,调用你的自定义转换器类,并且传入正则规则,也是可以使用的,当然自定义的转换器还有两个方法也是值得一提的:
1.to_python(self, value):当请求的url中包含的参数通过正则验证之后,会通过to_python这个方法返回给对应的视图
举个栗子:需求隐藏手机号的后四位替换成xxxx
from flask import Flask from werkzeug.routing import BaseConverter app = Flask(__name__) class RegexConverter(BaseConverter): """自定义转换器类""" def __init__(self, url_map, regex): # 调用父类的初始化方法 这里我们需要的第二个参数就是传给父类 super(RegexConverter, self).__init__(url_map) # 将正则表达式的参数保存到对象的属性中,flask会去使用这个属性来进行路由的正则匹配 print('init 执行了') self.regex = regex # 定义一个实例属性存储原手机号和加密后的手机号 self.phone = [] def to_python(self, value): print('to_python 执行了') # 将原有的手机号码存入集合中 self.phone.append(value) # 自制加密 self.value = value[0:8] + "XXXX" # 将加密的数据存入集合中 self.phone.append(self.value) return self.phone # 将自定义的转换器添加到flask的应用中 app.url_map.converters['re'] = RegexConverter # 这里模拟匹配手机号码 # 127.0.0.1:5000/send/138XXXXXXXX @app.route("/send/<re(r'1[34578]\d{9}'):phone_num>") def send_sms(phone_num): original_number = phone_num[0] encrypted_data = phone_num[1] return "%s send message to %s" % (original_number, encrypted_data) if __name__ == '__main__': # 查看路由信息 print(app.url_map) # 启动flask程序 app.run(debug=True)
返回结果:
如果传入的参数无法匹配正则就不会去执行to_python,如果没有重写to_python方法,默认会调用父类的to_python方法,然后将返回值给调用的视图函数:ps:在文档中为了不显得冗长,我删除了一个没用的视图,但是执行时候还在那所以这里会有两个视图
2.to_url(self, value):当使用到url_for的时候使用到
大致流程:定义了一个index函数用于跳转到send_sms中,这里index视图函数使用url_for 所以会调用to_url方法,首先访问127.0.0.1:5000/ 会跳转到send_sms
from flask import Flask, current_app, redirect, url_for from werkzeug.routing import BaseConverter app = Flask(__name__) class RegexConverter(BaseConverter): """自定义转换器类""" def __init__(self, url_map, regex): # 调用父类的初始化方法 这里我们需要的第二个参数就是传给父类 super(RegexConverter, self).__init__(url_map) # 将正则表达式的参数保存到对象的属性中,flask会去使用这个属性来进行路由的正则匹配 print('init 执行了') self.regex = regex self.phone = [] def to_python(self, value): print('to_python 执行了') self.phone.append(value) self.value = value[0:8] + "XXXX" self.phone.append(self.value) return self.phone def to_url(self, value): print('to_url 执行了') return value # 将自定义的转换器添加到flask的应用中 app.url_map.converters['re'] = RegexConverter # 这里模拟匹配手机号码 # 127.0.0.1:5000/send/138XXXXXXXX @app.route("/send/<re(r'1[34578]\d{9}'):phone_num>") def send_sms(phone_num): print('send_sms 视图函数执行') original_number = phone_num[0] encrypted_data = phone_num[1] return "%s send message to %s" % (original_number, encrypted_data) @app.route('/') def index(): # 使用反向解析到我们定义的send_sms视图函数 # 因为路径匹配规则中有phone_num 所以这里我们需要传参 print('index') url = url_for('send_sms', phone_num='18000000000') return redirect(url) if __name__ == '__main__': # 查看路由信息 print(app.url_map) # 启动flask程序 app.run(debug=True)
返回结果:访问127.0.0.1:5000/
后台查看一下执行流程: