一、请求调度
浏览器通过URL访问Flask服务器时,要通过URL和视图函数的映射关系表找到处理该URL的视图函数(该视图函数返回响应给浏览器),这个映射关系可以通过app.route修饰器建立。下面给出了上一篇文章中的helloworld例子
#coding:utf-8
from flask import Flask
app=Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello World!</h1>"
@app.route("/user/<name>")
def user(name):
return "<h1>Hello,%s</h1>"%name
if __name__=="__main__":
app.run(debug=True,host="0.0.0.0",port=8000)
例子中将程序根目录URL和视图函数index映射起来。可以在python命令行通过app.url_map查看这种映射关系:
HEAD,OPTIONS,GET是URL与视图函数的请求方法,HEAD和OPTIONS为Flask自动处理,功能分别为获取响应报文首部(即不发送报文主体)和询问访问某URL支持的方法。 可以通过route修饰器的methods参数为路由指定不同的请求方法,比较常用的是GET和POST方法。
二、程序和请求上下文
视图函数在处理请求时,有时需要获取一些其他对象信息,这个时候的处理方式是使用线程独立的全局变量而非传参,比如请求对象request等(其他上下文全局变量如下表)。上下文可以理解为对这些对象的引用池,方便我们随时访问这些变量。
变量名 | 上下文 | 说明 |
current_app | 程序上下文 | 当前激活的程序实例,比如helloworld例子中的app |
g | 程序上下文 | 用作处理请求时的临时存储,每次请求都重设 |
request | 请求上下文 | 请求对象,包含了客户端HTTP请求的内容,例如获取客户端请求报文头部中包含的 User-Agent信息:request.headers.get("User-Agent") |
session | 请求上下文 | 用户会话,字典格式,存储请求间需要记住的信息 |
从flask中引入上下文变量就可以使用这些变量,例如下面的视图函数返回请求中的User-Agent给客户端:
@app.route("/user-agent/")
def get_user_agent():
user_agent=request.headers.get("User-Agent")
return "<p>%s</p>"%user_agent
输入URL http://127.0.0.1:8000/user-agent/ 访问:
Falsk上下文在请求之前会先激活,请求处理完毕则会将其删除。
三、响应
响应即视图函数的返回值,上述例子中的返回都很简单,直接将html代码作为返回值。但是Flask Http协议的返回值中通常还会有以下几种不同的格式:
1.元组:(html字符串,状态码,返回报文首部信息)
状态响应码:Flask默认为200(故此时可省略),即请求成功处理。一些常用的状态码如下:
2xx成功:200(请求成功处理)、204(请求成功处理但无资源返回)、206(请求部分内容成功,在请求报文实体首部中包含需要的资源)
3xx重定向:301(永久性重定向,表示访问资源已经更新了URI,通常在返回报文首部信息增加Location提示新的URI,如果访问的URI保存为书签,则会被更新为新的URI)
302(临时性重定向,表示此次访问资源被重定向到新的URI,并非永久,不更新书签)
304(资源不满足客户端请求条件,比如请求的是某时间点后有更新则返回新资源,但资源在该时间点后无更新,被规在3xx,但和重定向没啥关系)
4xx客户端错误:400(客户端请求存在错误,应该修改请求后再次发送)、401(未授权)、403(禁止访问)、404(找不到访问的资源)
5xx服务器错误:500(服务器处理请求时出现错误)、503(服务器忙碌或者停机维护无法处理请求)
返回报文首部信息:比如包含重定向时,包含URI的Location信息,{"Location":"http:xxx"},很少用到,可省略。
2.Response对象
make_response()函数生成Response对象,函数接受1、2、3个参数,参数意义和元组形式一致,可以对响应进行一些设置后返回,比如设置cookie。
from flask import make_response
@app.route("/response/")
def response():
response=make_response("<h1>This response carries a cookie</h1>")
response.set_cookie("answer","42")
return response
浏览器访问后再次请求f12查看请求头中包含设置的cookie。
3.使用redirect函数
常用来跳转到重定向的url,比如return redirect("http://www.baidu.com/")
from flask import redirect
@app.route("/redirect")
def test_redirect():
return redirect("http://www.baidu.com/")
4.使用abort函数处理错误
通常用来抛出异常,把控制权交给web服务器返回异常,比如:abort(404)
5.使用渲染模板
前述的例子都太简单,通常一个http请求的处理可能会涉及复杂的业务逻辑和表现逻辑,比如在某网站注册账号的时候,通过POST方法提交一个表单给服务器,服务器需要通过表单内容来生成一个新用户并进行数据库相关操作,然后再给客户端返回一个处理完毕的响应。模板的作用是处理表现逻辑,把业务逻辑和表现逻辑分开,提升代码的可维护性。Flask使用了Jinja2模板引擎。
最简单的helloworld模板可以这样使用:在helloworld.py同一目录下创建templates文件夹,然后把模板文件放在文件夹中,比如'helloworld.html",然后通过Flask的render_template函数渲染模板,并作为视图函数返回值即可。
from flask import render_template
@app.route("/")
def index():
return render_template("helloworld.html")
模板文件包含响应文本:访问“http://127.0.0.1:8000”可以看到效果和原来一样~
复杂的响应文本中会包含逻辑语句以及变量,变量真实值可以通过render_template传递。使用真实值替换模板中的变量,得到最终的响应文本,这个过程即称为渲染模板。
三、模板
1.变量
模板中的变量使用双大括号占位+变量名表示:比如{{ name }}。我们可以把helloword例子的user视图函数替换成模板渲染后返回:
新增模板文件templates/user.html:
<h1>Hello,{{ name }}</h1>
user视图函数:
@app.route("/user/<name>")
def user(name):
return render_template("user.html",name=name)
访问url可以看到效果:
上述例子使用url中的<name>字符串作为变量传递给视图函数,视图函数又将之传递给模板渲染后返回响应。Jinja2可以识别复杂的对象,比如可以把一个用户对象传递给模板,然后在模板中访问对象的各个属性变量。例如 {{ user.username }} 获取user对象的username属性。
变量可以用过滤器修改,Jinja2提供了一些常用的过滤器,使用格式为{{ 变量名|过滤器名 }}比如要将上述模板中的name变量全部以大写字母显示,可以修改user.html如下:
效果:
常用的Jinja2过滤器有:
过滤器名 | 说明 |
safe | 渲染值时不转义,默认情况下安全起见,jinja2会将html标签在渲染时会被转义掉(比如左尖括号转义为<),使用这个过滤器可以保护这些标签不被过滤(对可信的变量比如服务器自己生成的html串采用) |
capitalize | 首字母大写,其他字母小写 |
lower | 小写形式 |
upper | 大写形式 |
title | 每个单词首字母大写 |
trim | 去掉值首尾空格 |
striptags | 把值中所有的HTML标签删除 |
2.控制语句,结构: {% 语句 %}
①条件语句
{% if name %}
Hello,{{ user }}!
{% else %}
Hello,Stranger!
{% endif %}
②for循环
<ul>
{% for user in users %}
<li>user.username</li>
{% endfor %}
</ul>
③宏 定义格式类似于函数,比如可以定义一个宏,然后再调用这个宏,使用宏定义的格式替换调用的部分
定义:关键字macro
{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
调用:{{ 宏名(参数) }}
<ul>
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>
也可以将宏单独保存在一个文件中,再调用的文件中使用 import语句引入。
{% import "_macro.html" as macro %}
<ul>
{% for comment in comments %}
{{ macro.render_comment(comment) }}
{% endfor %}
</ul>
④导入代码片,其作用是代码重用,如下可将_comments.html中的代码片段包含到模板文件中。
{% include "_comments.html" %}
⑤模板继承,比如同一个网站的不同网页通常有一些相同或相似的格式,这时候可以把这些相似的样式写在一个基模板中,同时预留可供扩展的block区域,其他页面可通过继承该模板,并在对应block区域扩展自己页面的内容:
基模板base.html:
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}-My App</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
继承自base.html的index.html:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ supper() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello World</h1>
{% endblock %}
子页面扩展的部分会替换基模板相同的block区域包含的部分,如果有内容需要继承基模板同时扩展新内容,使用supper()来获取基模板内容。