9.0 Flask and Server-Side Rendering
What is a web server?
- Web Server: 一个在连接到互联网的计算机上运行的程序,它响应来自网络上其他计算机(客户端)的请求 [cite: 3]。
- 通常,Web 服务器会监听特定的网络端口,等待客户端通过 HTTP (Hypertext Transfer Protocol) 或 HTTPS (Secure HTTP) 发送请求。
- 收到请求后,服务器会处理该请求(例如,检索文件、运行脚本、查询数据库),然后将 HTTP 响应发送回客户端,客户端通常是网页浏览器。
- 服务器端的 Web 开发很多时候是在命令行完成的,因为传统上服务器不需要图形用户界面 [cite: 3]。
Full-stack development
- Full-stack development: 指的是 Web 开发中涉及到的前端 (client-side) 和后端 (server-side) 技术的集合 [cite: 4]。
- Front-end technologies: 通常指 HTML, CSS, 和 JavaScript,用于在浏览器中创建用户界面和用户体验 [cite: 4]。
- Back-end technologies: 允许我们编写在服务器上运行的程序,处理应用程序逻辑、数据库交互、用户认证等任务 [cite: 4]。
- Stack: 指的是实现前端和后端所需的一整套技术列表 [cite: 4]。
Components of the full stack used in this course
本课程中使用的后端技术栈组件及其用途如下 [cite: 5]:
- Flask: 一个 Python 的微型 Web 框架 (micro framework)。它用于构建 Web 应用程序的后端逻辑。它包含一个用于开发的轻量级 Web 服务器。
- SQLite: 一个轻量级的磁盘文件数据库管理系统。用于存储和管理应用程序数据。
- AJAX (Asynchronous JavaScript and XML): 一种在网页加载后,在浏览器和服务器之间异步传输数据的方法。它允许网页在不重新加载整个页面的情况下更新部分内容。
- jQuery: 一个 JavaScript 库,简化了 HTML 文档遍历、事件处理、动画和 AJAX 交互,方便在客户端进行 AJAX 请求。
How Flask app handles routes
- Routes in Flask 将 URL 映射到 Python 函数 (称为视图函数或处理函数)。
- 当 Flask 应用收到一个 HTTP 请求时,它会查找哪个视图函数注册了与请求 URL 相匹配的路由。
- 这是通过使用 decorator
@app.route('/url_path')
来实现的,该装饰器放置在视图函数定义之前 [cite: 11]。
app
是 Flask 应用的一个实例 (app = Flask(__name__)
) [cite: 11]。'/url_path'
是希望与该函数关联的 URL 路径。
- 当用户访问该 URL 时,Flask 会执行关联的函数,并将该函数的返回值作为 HTTP 响应发送给客户端 [cite: 11]。
- Endpoints: 使用
@app.route(...)
注解的函数被称为端点 (endpoints) [cite: 14]。
一个端点可以对应多个 URL:
路由可以接受参数,例如
<variable_name>
,这些参数会自动传递给视图函数 [cite: 14]:
- 可以指定参数类型,如
<int:userid>
[cite: 14]。
- HTTP methods: 默认情况下,端点只响应 GET 请求。可以使用
methods
参数指定其他 HTTP 方法,如 POST [cite: 15]:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# process login
pass
else:
# show login form
pass
- url_for(): 用于动态生成指定端点的 URL,避免在代码中硬编码 URL [cite: 15, 16]。这使得 URL 更改时维护更容易。
from flask import url_for
url_for('index') # 会返回 '/' 或 '/index' 的 URL
url_for('show_user_profile', username='JohnDoe') # 会返回 '/user/JohnDoe'
Differences between server-side and client-side rendering
- Server-side rendering (SSR) [cite: 17]:
- Process: 当客户端(浏览器)请求一个页面时,服务器处理请求,执行必要的逻辑(例如,从数据库获取数据),将数据填充到 HTML 模板中,生成完整的 HTML 页面,然后将这个 HTML 发送给客户端。浏览器接收到 HTML 后直接渲染。
- Traditional Page Lifecycle: 初始请求获取 HTML。表单提交 (POST) 后,服务器处理数据,然后通常返回一个新的完整 HTML 页面,导致页面重新加载 [cite: 17]。
- Pros: 有利于 SEO (搜索引擎优化),因为搜索引擎可以直接抓取到完整的页面内容。首次内容加载速度可能更快 (First Contentful Paint)。
- Cons: 每次交互或页面变化都可能需要重新从服务器加载整个页面,服务器负载较高。
- Client-side rendering (CSR) [cite: 17]:
- Process: 服务器初始发送一个最小的 HTML骨架(通常包含 JavaScript 文件的链接)。浏览器下载并执行这些 JavaScript 文件。JavaScript 代码随后负责获取数据(通常通过 AJAX 请求从服务器获取 JSON 数据),并在客户端动态生成和渲染 HTML 内容。
- SPA (Single Page Application) Lifecycle: 初始请求获取 HTML 骨架和 JavaScript。后续的数据交互通过 AJAX 请求(服务器返回 JSON 数据),JavaScript 在客户端更新页面内容,无需完全重新加载页面 [cite: 17]。
- Pros: 页面交互更流畅,用户体验类似桌面应用。服务器负载较低,因为它主要提供数据 API。更灵活,支持非浏览器设备。
- Cons: 初始加载时间可能较长,因为需要下载和执行 JavaScript。SEO 可能更复杂(尽管现在搜索引擎对 JavaScript 的处理能力有所提高)。
- Flask 支持这两种渲染方式 [cite: 17]。
Purpose of templates
- Templates 的主要目的是将应用程序的 presentation logic (如何显示数据) 与 application logic (数据处理和业务规则) 分离开来 [cite: 19, 20]。
- 在 Web 开发中,模板通常是 HTML 文件,其中包含特殊的占位符 (placeholders) 和一些简单的逻辑结构 (如循环和条件语句)。
- Dynamic HTML Generation: 服务器端的渲染引擎 (在 Flask 中是 Jinja2 [cite: 20]) 会获取模板文件,并将应用程序传递过来的动态数据填充到这些占位符中,最终生成一个完整的 HTML 页面发送给客户端。
- 例如,一个模板可能有一个占位符
{{ username }}
。在渲染时,username
会被实际的用户名替换 [cite: 21]。 - Reusability: 模板可以被重用。例如,可以创建一个基础模板 (base template) 包含网站的通用布局(如页眉、页脚、导航栏),其他特定页面可以继承这个基础模板并只填充其特有的内容部分 (
{% block content %}
和{% extends "base.html" %}
in Jinja) [cite: 26, 27]。这遵循了 DRY (Don't Repeat Yourself) 原则 [cite: 27]。 - Maintainability: 分离关注点使得代码更易于维护。设计师可以专注于 HTML/CSS,而开发者可以专注于 Python 逻辑。
- Jinja2 features:
- Variables:
{{ variable_name }}
[cite: 21] - Control Structures:
- Conditionals:
{% if condition %}
,{% else %}
,{% elif condition %}
,{% endif %}
[cite: 23] - Loops:
{% for item in items %}
,{% endfor %}
[cite: 22, 24, 25]
- Conditionals:
- Template Inheritance:
{% extends "base.html" %}
和{% block block_name %}
[cite: 26, 27]
Purpose of FlaskForms library (Flask-WTF)
- Flask-WTF 是一个与 Flask 集成的扩展,它简化了在 Flask 应用中使用 WTForms 库处理 Web 表单的过程 [cite: 29]。
- Purpose:
Form Definition: 允许开发者使用 Python 类来定义表单的结构和字段。每个字段可以指定类型(如字符串、整数、布尔值)和验证器 (validators) [cite: 31]。
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Email class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()]) remember_me = BooleanField('Remember Me') submit = SubmitField('Sign In')
Rendering Forms in Templates: 可以轻松地将表单对象传递给 Jinja2 模板,并在模板中渲染 HTML 表单元素。Flask-WTF 会处理字段的 HTML 生成,包括标签、输入框等 [cite: 32]。
Data Validation: 当表单提交后 (通常是 POST 请求),Flask-WTF 可以自动验证提交的数据是否符合在表单类中定义的验证规则 (e.g.,
DataRequired
,Email
,Length
) [cite: 34]。form = LoginForm() if form.validate_on_submit(): # Process data from form.username.data, form.password.data flash('Login requested for user {}, remember_me={}'.format( form.username.data, form.remember_me.data)) return redirect(url_for('index')) return render_template('login.html', title='Sign In', form=form)
CSRF Protection: 自动处理跨站请求伪造 (Cross-Site Request Forgery) 保护。需要在 Flask 应用配置中设置
SECRET_KEY
[cite: 30],并在模板中使用{{ form.hidden_tag() }}
[cite: 32]。Populating Forms: 可以用数据预填充表单字段。
Error Handling: 验证失败时,错误信息可以方便地在模板中显示给用户 [cite: 32]。
You are expected to be able to read, critique and fix basic Flask code and Jinja templates but you are not expected to write them from scratch.