文章目录
FLask程序与HTTP的沟通
Flask web程序的工作流程
注意:这里的服务器指的是处理请求和响应的Web服务器,比如上一章介绍的开发服务器,而不是指物理层面上的服务器主机。
- 当用户访问一个URL,浏览器便生成对应的HTTP请求,经由互联网发送到对应的Web服务器。
- Web服务器接收请求,通过WSGI(目前还不知道是啥)将HTTP格式的请求数据转换成我们的Flask程序能够使用的Python数据。
- 在程序中,Flask根据请求的URL执行对应的视图函数(app.route(’/’)这些),获取返回值生成响应。
- 响应依次经过WSGI转换生成HTTP响应,再经由Web服务器传递,最终被发出请求的客户端接收。
- 浏览器渲染响应中包含的HTML和CSS代码,并执行JavaScript代码,最终把解析后的页面呈现在用户浏览器的窗口中。
请求方式
当你直接访问一个 页面时,请求的方法是GET;
当你在某个页面填写了表单并提交时,请 求方法则通常为POST。
网址输入请求字符串
代码
from flask import Flask,request
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/hello')
def hello():
name = request.args.get('name')
return 'hello,%s'%name
需要注意的是,当我们从request对象的属性(比如files、form、args)中直接使用键作为索引获取数据时(比如request.args[‘name’])
如果没有对应的键,那么会返回HTTP 400错误响应(Bad Request,表示请求无效),而不是抛出KeyError异常。
为了避免这个错误,我们应该使用get()方法获取数据,如果没有对应的值则返回None;
get()方法的第二个参数可以设置默认值,比如 requset.args.get(‘name’,‘Human’)。
输入参数类型的处理
@app.route('/goback/<int:year>')
def fo_back(year):
return 'Welcome to ' + str((2018 - year))
<int:year>表示为year变量添加了一个int转换器, Flask在解析这个URL变量时会将其转换为整型。
同时还有一些其它的类型可供转换
比较特别的一个类型转换
在用法上唯一特别的是any转换器,你需要在转换器后添加括号来给出可选值
即“<any(value1,value2,…):变量名>”,比如:
@app.route('/colors/<any(blue, white, red):color>')
any的类型是指,如果将color参数部分替换为any转换器中设置的可选值以外的任意字符,均会获得404错误响应。
如果你想在any转换器中传入一个预先定义的列表,可以通过格式化字符串的方式(使用%或是format()函数)来构建URL规则字符串,比如
colorslist = ['blue', 'white', 'red']
@app.route('/colors/<any(%s):color>'%str(colorslist)[1:-1])
def colorPrint(color):
return 'The color is '+color
设置监听方法
当某个请求的方法不符合要求时,请求将无法被正常处理。
比如,在提交表单时通常使用POST方法,而如果提交的目标URL对应的视图函数只允许GET方法,这时Flask会自动返回一个405错误响应(Method Not Allowed,表示请求方法不允许)
同时:flask routes命令能让你查看哪些视图函数允许哪些方法
请求钩子(Hook)
有时我们需要对请求进行预处理和后处理,这时可以使用Flask提供的一些请求钩子 (Hook),它们可以用来注册在请求处理的不同阶段执行的处理函数 (或称为回调函数,即Callback)。
以before_request钩子(请求之前) 为例,当你对一个函数附加了app.before_request装饰器后,就会将这个函数注册为before_request处理函数,每次执行请求前都会触发所有 before_request处理函数。
这些钩子使用起来和app.route()装饰器基本相同,每个钩子可以注册任意多个处理函数,函数名并不是必须和钩子名称相同。
具体实现如下:
@app.before_request
def do_something():
请求钩子的常见运用场景
- ·before_first_request:在玩具程序中,运行程序前我们需要进行一 些程序的初始化操作,比如创建数据库表,添加管理员用户。这些工作 可以放到使用before_first_request装饰器注册的函数中。
- before_request:比如网站上要记录用户最后在线的时间,可以通过用户最后发送的请求时间来实现。为了避免在每个视图函数都添加更新在线时间的代码,我们可以仅在使用before_request钩子注册的函数中调用这段代码
- after_request:我们经常在视图函数中进行数据库操作,比如更 新、插入等,之后需要将更改提交到数据库中。提交更改的代码就可以 放到after_request钩子注册的函数中。
另一种常见应用
另一种常见的应用是建立数据库连接,通常会有多个视图函数需要建立和关闭数据库连接,而这些操作实质上基本相同。
一个理想的解决方法是在请求之前(before_request)建立连接,在请求之后(teardown_request) 关闭连接。
于是通过在使用相应的请求钩子注册的函数中添加代码就可以实现。
注意:after_request钩子和after_this_request钩子必须接收一个响应类对象 作为参数,并且返回同一个或更新后的响应对象。
在Flask中生成响应
视图函数可以返回最多由三个元素组成的元组:响应主 体、状态码、首部字段。其中首部字段可以为字典,或是两元素元组组 成的列表。
重定向
在Web程序中,我们经常需要进行重定向。比如,当某个用户在没有经过认证的情况下访问需要登录后才能访问的资源,程序通常会重定向到登录页面。
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/hi/')
def hello_world1():
return 'Hi!'
@app.route('/hello')
def hello():
//默认状态码为302
return redirect('hi')
上图代码既可以填写自己的视图(如’hi’)也可以填写网址(如’www.baidu.com’)
手动返回某类响应
如果想手动返回错误响应,较为方便的办法是使用Flask提供的abort()函数。
在abort()函数中传入状态码即可返回对应的错误响应
@app.route('/404')
def not_found():
abort(403)
注意:abort()函数前不需要使用return语句,但一旦abort()函数被调用,abort()函数之后的代码将不会被执行
响应格式
在HTTP响应中,数据可以通过多种格式传输。大多数情况下,我们会使用HTML格式,这也是Flask中的默认设置。
Content-Type: text/html; charset=utf-8
在特定的情况下,我们也会使用其他格式。
如果你想使用其他MIME类型,可以通过Flask提供的 make_response()方法生成响应对象,传入响应的主体作为参数,然后使用响应对象的mimetype属性设置MIME类型,比如:
from flask import make_response
@app.route('/foo')
def foo():
response = make_response('Hello, World!')
response.mimetype = 'text/plain'
return response
同时也可以直接设置首部字段,比如
response.headers['Content- Type']='text/xml;charset=utf-8'。
Cookie
在Flask中,如果想要在响应中添加一个cookie,最方便的方法是使用Response类提供的set_cookie()方法。
要使用这个方法,需要先使用make_response()方法手动生成一个响应对象,传入响应主体作为参数。
这个响应对象默认实例化内置的Response类。
代码如下
@app.route('/set/<name>')
def set_cookie(name):
#这行语句有问题 如果是这句话 那么访问的就是/set/hello 会造成多次重定向 所以正确的前面要加上/
#response = make_response(redirect('hello'))
response = make_response(redirect('/hello'))
response.set_cookie('name',name)
return response
这个时候查看网站的cookie,就会发现多了name
cookie的读取和查询
from flask import Flask, request, redirect, make_response
app = Flask(__name__)
@app.route('/')
@app.route('/hello')
def hello():
#从cookie里获取
name = request.args.get('name')
if name is None:
#从cookie里获取name的值
name = request.cookies.get('name','Human')
return 'Hello '+name
从之前的set_cookie里获取到cookie的name值,再输出出来,如果没有这个cookie,那就是Human。