Python-Web学习-Flask框架初探

Python有上百种Web开发框架,有很多成熟的模板技术,选择Python开发Web应用,不但开发效率高,而且运行速度快。

WSGI

Web Server Gateway Interface:WSGI 规范了一种简单的接口,描述了web server如何与web application交互、web application如何处理请求。实现了WSGI的模块/库有wsgiref(python内置)、werkzeug.serving、twisted.web等

应用程序/框架端(Web framework/app)

符合WSGI标准的application接口应该实现为一个可调用对象(callable object),例如函数、方法、类、含call方法的实例。这个可调用对象可以接收2个参数:

  • environ:一个包含所有HTTP请求信息的dict对象,常用的有:environ[‘REQUEST_METHOD’]、environ[‘PATH_INFO’]
  • start_response:一个发送HTTP响应的函数。

服务器端/网关端(Web server)

每一次,当HTTP客户端冲着应用程序发来一个请求,服务器/网关都会调用应用程序可调用者(callable)。

中间件(Middleware)

中间件位于WSGI server和WSGI application之间,包装了应用程序,可以实现:

  • 在相应地重写environ变量之后,根据目标URL地址将请求路由到不同的应用程序对象。
  • 允许多个应用程序或框架在同一个进程中并行运行。
  • 通过在网络上转发请求和应答,实现负载均衡和远程处理。
  • 对上下文(content)进行后加工(postprocessing),比如应用xsl样式表等。

示例

以python内置的wsgiref作为服务器端为例:

  • 符合WSGI接口的application()方法可以收这两个参数,最后将environment字典的内容返回给客户端。
  • 中间件:实现小写转大写
  • 服务器端:wsgiref的make_server方法调用中间件Upperware(application)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    from wsgiref.simple_server import make_server
    # 应用程序端
    def application(environ, start_response):
    response_body = [
    '%s: %s' % (key, value) for key, value in sorted(environ.items())
    ]
    response_body = '\n'.join(response_body) # 由于下面将Content-Type设置为text/plain,所以`\n`在浏览器中会起到换行的作用
    status = '200 OK'
    response_headers = [
    ('Content-Type', 'text/plain'),
    ('Content-Length', str(len(response_body)))
    ]
    start_response(status, response_headers)
    return [response_body]
    # 中间件
    class Upperware:
    def __init__(self, app):
    self.wrapped_app = app
    def __call__(self, environ, start_response):
    for data in self.wrapped_app(environ, start_response):
    yield data.upper()
    # 创建一个服务器,IP地址为空,端口是8000,处理函数是application或者是中间件:
    wrapped_app = Upperware(application)
    # httpd = make_server('', 8000, application)
    httpd = make_server('', 8000, wrapped_app)
    print('Serving HTTP on port 8000...')
    # 开始监听HTTP请求:
    httpd.serve_forever()

模板

Web App不仅仅是处理逻辑,展示给用户的页面也非常重要。在函数中返回一个包含HTML的字符串,简单的页面还可以,但是,复杂的页面在Python的字符串中很难正确地写出来。

1
2
3
4
5
6
response_body = '''<form action="/signin" method="post">
<p><input name="username"></p>
<p><input name="password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
return [response_body]

所以,为了解决上述问题,模板技术出现了,类似于java web中的JSP。用于创建 HTML、XML 等其它标记格式文件,这些文件能通过 HTTP 响应返回给用户。主要以下几种:

  • Jinja2:{ { name } }表示一个需要替换的变量。{ % … % }表示指令,解决循环、条件判断等指令语句
  • Mako:用<% … %>和${xxx}的一个模板
  • Cheetah:也是用<% … %>和${xxx}的一个模板
  • Django:Django是一站式框架,内置一个用{ % … %}和{ { xxx } }的模板。

备注:文中的两层大括号,由于hexo发布博客bug的原因,两个大括号之间加入了空格{ {,{ %也多加了个空格。

web框架

一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应。处理100个不同的URL?最简单的想法是从environ变量里取出HTTP请求的信息,然后逐个判断。只是这么写下去代码是肯定没法维护了。

1
2
3
4
5
6
7
8
def application(environ, start_response):
method = environ['REQUEST_METHOD']
path = environ['PATH_INFO']
if method=='GET' and path=='/':
return handle_home(environ, start_response)
if method=='POST' and path='/signin':
return handle_signin(environ, start_response)
...

我们需要在WSGI接口之上能进一步抽象,让我们专注于用一个函数处理一个URL,至于URL到函数的映射,就交给Web框架来做。

常见的Python Web框架有:

  • Django:全能型Web框架,一站式,自带模板、ORM、Session、Authentication等
  • Flask:轻量级Web应用框架,扩展性强,附加功能都通过extension增加
  • web.py:一个小巧的Web框架;
  • Bottle:和Flask类似的Web框架;
  • Tornado:Facebook的开源异步Web框架。

Flask

Flask是一个使用Python编写的轻量级Web应用框架。基于:

  • Werkzeug WSGI工具箱
  • Jinja2 模板引擎

Flask也被称为“microframework”,因为它使用简单的核心,其他功能用extension增加:ORM、窗体验证工具、文件上传、各种开放式身份验证技术。

模板和静态文件放在应用的 Python 源代码树的子目录中,名称分别为 templates 和 static

安装

虚拟环境:可以使用pip安装,也可以用ubuntu系统中的软件包管理器

1
2
sudo apt install virtualenv #ubuntu apt
sudo pip install virtualenv #pip

flask安装

1
sudo pip install Flask

示例

Flask通过request.form[‘name’]来获取表单的内容,通过render_template()函数来实现模板的渲染,在框架内部组装出一个dict作为Model作为render_template的参数传递到模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('home.html')
@app.route('/signin', methods=['GET'])
def signin_form():
return render_template('form.html')
@app.route('/signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
if username == 'admin' and password == 'password':
return render_template('signin-ok.html', username=username)
return render_template('form.html', message='Bad username or password', username=username)
if __name__ == '__main__':
app.run()

form.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
<title>Please Sign In</title>
</head>
<body>
{ % if message %}
<p style="color:red">{ { message }}</p>
{ % endif %}
<form action="/signin" method="post">
<legend>Please sign in:</legend>
<p><input name="username" placeholder="Username" value="{ { username }}"></p>
<p><input name="password" placeholder="Password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>
</body>
</html>

sign-ok.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>Welcome, { { username }}</title>
</head>
<body>
<p>Welcome, { { username }}!</p>
</body>
</html>

home.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>Home</title>
</head>
<body>
<h1 style="font-style:italic">Home</h1>
</body>
</html>

参考

理解Python WSGI
PEP 333 - Python Web Server Gateway Interface v1.0 中文版
Python wsgiref 模块源码浅析
廖雪峰Python教程-使用模板
https://dormousehole.readthedocs.io/en/latest/

文章目录
  1. 1. WSGI
    1. 1.1. 应用程序/框架端(Web framework/app)
    2. 1.2. 服务器端/网关端(Web server)
    3. 1.3. 中间件(Middleware)
    4. 1.4. 示例
  2. 2. 模板
  3. 3. web框架
  4. 4. Flask
    1. 4.1. 安装
    2. 4.2. 示例
  5. 5. 参考
|