跳转至

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:

    @app.route('/')
    @app.route('/index')
    def index():
        return "Hello, World!"
    
  • 路由可以接受参数,例如 <variable_name>,这些参数会自动传递给视图函数 [cite: 14]:

    @app.route('/user/<username>')
    def show_user_profile(username):
        return f'User {username}'
    
  • 可以指定参数类型,如 <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]
  • 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:
  1. 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')
    
  2. Rendering Forms in Templates: 可以轻松地将表单对象传递给 Jinja2 模板,并在模板中渲染 HTML 表单元素。Flask-WTF 会处理字段的 HTML 生成,包括标签、输入框等 [cite: 32]。

    <form method="POST" action="">
        {{ form.hidden_tag() }} {#  For CSRF protection #}
        <p>
            {{ form.username.label }}<br>
            {{ form.username(size=32) }}<br>
            {% for error in form.username.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.submit() }}</p>
    </form>
    
  3. 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)
    
  4. CSRF Protection: 自动处理跨站请求伪造 (Cross-Site Request Forgery) 保护。需要在 Flask 应用配置中设置 SECRET_KEY [cite: 30],并在模板中使用 {{ form.hidden_tag() }} [cite: 32]。

  5. Populating Forms: 可以用数据预填充表单字段。

  6. 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.