1-Flask快速入门

1 Flask 介绍

# 0 Flask是一个用Python编写的Web应用框架。它由Armin Ronacher开发,他领导着一个名为Pocco的国际Python爱好者团队。Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。两者都是Pocco项目

# 1 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器

# 2 “微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握

# 3 默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用

# 4 Flask depends on the Werkzeug WSGI toolkit, the Jinja template engine, and the Click CLI toolkit

1.1 Werkzeug介绍

Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等

Werkzeug is a comprehensive WSGI web application library. It began as a simple collection of various utilities for WSGI applications and has become one of the most advanced WSGI utility libraries.
Werkzeug 是一个综合性 WSGI Web 应用程序库。它最初是 WSGI 应用程序的各种实用程序的简单集合,现已成为最先进的 WSGI 实用程序库之一。

Werkzeug doesn’t enforce any dependencies. It is up to the developer to choose a template engine, database adapter, and even how to handle requests
Werkzeug 不强制执行任何依赖关系。由开发人员选择模板引擎、数据库适配器,甚至如何处理请求

# https://werkzeug.palletsprojects.com/en/3.0.x/
from werkzeug.wrappers import Request, Response

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, hello)

1.2 wsgiref 介绍

最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块

from wsgiref.simple_server import make_server

def mya(environ, start_response):
    print(environ)
    start_response('200 OK', [('Content-Type', 'text/html')])
    if environ.get('PATH_INFO') == '/index':
        with open('index.html','rb') as f:
            data=f.read()

    elif environ.get('PATH_INFO') == '/login':
        with open('login.html', 'rb') as f:
            data = f.read()
    else:
        data=b'<h1>Hello, web!</h1>'
    return [data]

if __name__ == '__main__':
    myserver = make_server('', 8011, mya)
    print('监听8010')
    myserver.serve_forever()

1.3 Jinja2 介绍

Jinja is a fast, expressive, extensible templating engine. Special placeholders in the template allow writing code similar to Python syntax. Then the template is passed data to render the final document
Jinja 是一个快速、富有表现力、可扩展的模板引擎。模板中的特殊占位符允许编写类似于 Python 语法的代码。然后向模板传递数据以渲染最终文档

# https://jinja.palletsprojects.com/en/3.1.x/

1.4 click介绍

# 1 Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It’s the “Command Line Interface Creation Kit”. It’s highly configurable but comes with sensible defaults out of the box
Click 是一个 Python 包,用于以可组合的方式使用尽可能少的代码创建漂亮的命令行界面。它是“命令行界面创建工具包”。它具有高度可配置性,但具有开箱即用的合理默认值

# 2  It aims to make the process of writing command line tools quick and fun while also preventing any frustration caused by the inability to implement an intended CLI API
它的目的是使编写命令行工具的过程变得快速而有趣,同时也防止因无法实现预期的 CLI API 而造成的任何挫败感

# 3  Click in three points:
  arbitrary nesting of commands	
  automatic help page generation
  supports lazy loading of subcommands at runtime 
Click三点:
  命令的任意嵌套
  自动生成帮助页面
  支持运行时延迟加载子命令

  

https://click.palletsprojects.com/en/8.1.x/
# pip3 install click
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',help='The person to greet.')
def hello(count, name):
    for x in range(count):
        click.echo(f"Hello {name}!")

if __name__ == '__main__':
    hello()


# 1  python3 app.py --count=3
# 2  python3 app.py --help
# 3  python3 app.py --count=3 --name=lqz

2 Flask 安装

2.1 Python 版本

We recommend using the latest version of Python. Flask supports Python 3.8 and newer

2.2 依赖

必须依赖

安装 Flask 时会自动安装这些依赖

  • Werkzeug implements WSGI, the standard Python interface between applications and servers.
  • Jinja is a template language that renders the pages your application serves.
  • MarkupSafe comes with Jinja. It escapes untrusted input when rendering templates to avoid injection attacks.
  • ItsDangerous securely signs data to ensure its integrity. This is used to protect Flask’s session cookie.
  • Click is a framework for writing command line applications. It provides the flask command and allows adding custom management commands.
  • Blinker provides support for Signals.

可选依赖

这些依赖不会自动安装。如果您安装它们,Flask 将检测并使用它们

# pip3 install python-dotenv
import os
from dotenv import load_dotenv
from dotenv import dotenv_values
## 1 加载配置文件
# 必须在根路径下新建一个 .env 的文件,并写入配置才能返回True,会把.env下的配置文件设置进环境变量
res=load_dotenv()  # take environment variables from .env
print(res)
print(os.environ.get('DOMAIN'))
# You will probably want to add .env to your .gitignore, especially if it contains secrets like a password.


## 2 获取环境变量字典
config = dotenv_values(".env")
print(config)
print(config.get('DOMAIN'))
# pip3 install watchdog
# 当前目录下文件修改会被监控到,打印日志
import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = sys.argv[1] if len(sys.argv) > 1 else '.'
    event_handler = LoggingEventHandler()
    observer = Observer()
    observer.schedule(event_handler, path, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

2.3 虚拟环境

Use a virtual environment to manage the dependencies for your project, both in development and in production.
# 在开发和生产中,使用虚拟环境来管理项目的依赖关系

What problem does a virtual environment solve? The more Python projects you have, the more likely it is that you need to work with different versions of Python libraries, or even Python itself. Newer versions of libraries for one project can break compatibility in another project.
# 虚拟环境解决什么问题?您拥有的 Python 项目越多,您就越有可能需要使用不同版本的 Python 库,甚至是 Python 本身。一个项目的较新版本的库可能会破坏另一项目的兼容性。

Virtual environments are independent groups of Python libraries, one for each project. Packages installed for one project will not affect other projects or the operating system’s packages.
# 虚拟环境是一组独立的 Python 库,每个项目对应一个。为一个项目安装的软件包不会影响其他项目或操作系统的软件包

Python comes bundled with the venv module to create virtual environments.
# Python 使用 venv 模块来创建虚拟环境

3.2.1 Mac/linux

# 创建虚拟环境
mkdir myproject
cd myproject
python3 -m venv .venv
# 激活虚拟环境
. .venv/bin/activate

3.2.2 Win

# 创建虚拟环境
mkdir myproject
cd myproject
py -3 -m venv .venv
# 激活虚拟环境
.venv\Scripts\activate

2.4 安装

pip install Flask

3 快速入门

3.1 简单demo

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"
  
#1 首先我们导入 Flask 类。此类的一个实例将是我们的 WSGI 应用程序。
#2 接下来我们创建该类的一个实例。第一个参数是应用程序的模块或包的名称。 __name__ 是一个方便的快捷方式,适用于大多数情况。这是必要的,以便 Flask 知道在哪里寻找模板和静态文件等资源。
#3 然后我们使用route()装饰器告诉Flask什么URL应该触发我们的函数。
#4 该函数返回我们想要在用户浏览器中显示的消息。默认内容类型是 HTML,因此字符串中的 HTML 将由浏览器呈现


Save it as hello.py or something similar. Make sure to not call your application flask.py because this would conflict with Flask itself.

# 运行当前项目
python -m flask --app hello run
# 或者
flask --app hello run
flask run --host=0.0.0.0

# 注意:
As a shortcut, if the file is named app.py or wsgi.py, you don’t have to use --app

3.2 调试模式

# 运行时,开启debug模式
flask --app hello run --debug

The flask run command can do more than just start the development server. By enabling debug mode, the server will automatically reload if code changes, and will show an interactive debugger in the browser if an error occurs during a request

image

注意:

The debugger allows executing arbitrary Python code from the browser. It is protected by a pin, but still represents a major security risk. Do not run the development server or debugger in a production environment.

3.3 Pycharm运行Flask项目

image

image

3.4 Html Escaping

When returning HTML (the default response type in Flask), any user-provided values rendered in the output must be escaped to protect from injection attacks. HTML templates rendered with Jinja, introduced later, will do this automatically.

返回 HTML(Flask 中的默认响应类型)时,必须对输出中呈现的任何用户提供的值进行转义,以防止注入攻击。使用 Jinja 渲染的 HTML 模板(稍后介绍)将自动执行此操作

escape(), shown here, can be used manually. It is omitted in most examples for brevity, but you should always be aware of how you’re using untrusted data.

此处显示的 escape() 可以手动使用。为了简洁起见,大多数示例中都省略了它,但您应该始终了解如何使用不受信任的数据

If a user managed to submit the name <script>alert("bad")</script>, escaping causes it to be rendered as text, rather than running the script in the user’s browser

如果用户设法提交名称 ,转义会导致其呈现为文本,而不是在用户的浏览器中运行脚本

from flask import Flask
from markupsafe import escape
app = Flask(__name__)
@app.route("/<path:name>")
def hello(name):
    return f"Hello, {escape(name)}!"
    # return f"Hello, {name}!"
# 用户从浏览器中访问:http://127.0.0.1:5000/<script>alert("bad")</script>

3.5 路由

Modern web applications use meaningful URLs to help users. Users are more likely to like a page and come back if the page uses a meaningful URL they can remember and use to directly visit a page.

现代 Web 应用程序使用有意义的 URL 来帮助用户。如果该页面使用了他们可以记住并用于直接访问该页面的有意义的 URL,则用户更有可能喜欢该页面并返回

Use the route() decorator to bind a function to a URL.

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello, World'

You can do more! You can make parts of the URL dynamic and attach multiple rules to a function

你可以做更多!您可以将 URL 的一部分设为动态并将多个规则附加到一个函数

3.5.1 可变路由规则(Variable Rules)

You can add variable sections to a URL by marking sections with <variable_name>. Your function then receives the <variable_name> as a keyword argument. Optionally, you can use a converter to specify the type of the argument like <converter:variable_name>

您可以通过用 <variable_name> 标记部分来将变量部分添加到 URL。然后,您的函数接收 <variable_name> 作为关键字参数。或者,您可以使用转换器来指定参数的类型,例如 converter:variable_name

from markupsafe import escape

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return f'User {escape(username)}'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return f'Post {post_id}'

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return f'Subpath {escape(subpath)}'

Converter types(转换器类型):

string (default) accepts any text without a slash 默认值,接收不带 / 的字符串
int accepts positive integers 接收正整数
float accepts positive floating point values 接收小数
path like string but also accepts slashes 接收带 / 的字串
uuid accepts UUID strings 接受uuid的字符串

3.5.2 Unique URLs / Redirection Behavior(独特的 URL/重定向行为)

The following two rules differ in their use of a trailing slash.

以下两条规则的不同之处在于尾部斜杠的使用。

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

The canonical URL for the projects endpoint has a trailing slash. It’s similar to a folder in a file system. If you access the URL without a trailing slash (/projects), Flask redirects you to the canonical URL with the trailing slash (/projects/).

项目端点的规范 URL 结尾有一个斜杠。它类似于文件系统中的文件夹。如果您访问不带尾部斜杠 (/projects) 的 URL,Flask 会将您重定向到带尾部斜杠 (/projects/) 的规范 URL

The canonical URL for the about endpoint does not have a trailing slash. It’s similar to the pathname of a file. Accessing the URL with a trailing slash (/about/) produces a 404 “Not Found” error. This helps keep URLs unique for these resources, which helps search engines avoid indexing the same page twice

about 端点的规范 URL 没有尾部斜杠。它类似于文件的路径名。访问带有尾部斜杠 (/about/) 的 URL 会产生 404“Not Found”错误。这有助于保持这些资源的 URL 唯一,从而帮助搜索引擎避免对同一页面建立两次索引

3.5.3 URL Building(路由反向解析)

To build a URL to a specific function, use the url_for() function. It accepts the name of the function as its first argument and any number of keyword arguments, each corresponding to a variable part of the URL rule. Unknown variable parts are appended to the URL as query parameters.

要构建特定函数的 URL,请使用 url_for() 函数。它接受函数名称作为其第一个参数和任意数量的关键字参数,每个参数对应于 URL 规则的变量部分。未知的变量部分作为查询参数附加到 URL 中

Why would you want to build URLs using the URL reversing function url_for() instead of hard-coding them into your templates?

为什么要使用 URL 反转函数 url_for() 构建 URL,而不是将它们硬编码到模板中

  1. Reversing is often more descriptive than hard-coding the URL (逆向通常比硬编码 URL 更具描述性)
  2. You can change your URLs in one go instead of needing to remember to manually change hard-coded URLs.(您可以一次性更改 URL,而无需记住手动更改硬编码 URL)
  3. URL building handles escaping of special characters transparently.(URL 构建透明地处理特殊字符的转义)
  4. The generated paths are always absolute, avoiding unexpected behavior of relative paths in browsers.(生成的路径始终是绝对路径,避免浏览器中相对路径的意外行为。)
  5. If your application is placed outside the URL root, for example, in /myapplication instead of /, url_for() properly handles that for you.(如果您的应用程序位于 URL 根目录之外,例如位于 /myapplication 而不是 / 中,则 url_for() 会为您正确处理该问题)

For example, here we use the test_request_context() method to try out url_for(). test_request_context() tells Flask to behave as though it’s handling a request even while we use a Python shell. See Context Locals.

例如,这里我们使用 test_request_context() 方法来尝试 url_for()。 test_request_context() 告诉 Flask 即使在我们使用 Python shell 时也表现得像在处理请求一样

from flask import url_for

@app.route('/')
def index():
    return 'index'

@app.route('/login')
def login():
    return 'login'

@app.route('/user/<username>')
def profile(username):
    return f'{username}\'s profile'

with app.test_request_context():
    print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next='/'))
    print(url_for('profile', username='John Doe'))
    
'''
/
/login
/login?next=/
/user/John%20Doe
'''

3.5.4 HTTP Methods(请求方法)

Web applications use different HTTP methods when accessing URLs. You should familiarize yourself with the HTTP methods as you work with Flask. By default, a route only answers to GET requests. You can use the methods argument of the route() decorator to handle different HTTP methods.

Web 应用程序在访问 URL 时使用不同的 HTTP 方法。在使用 Flask 时,您应该熟悉 HTTP 方法。默认情况下,路由仅响应 GET 请求。您可以使用route()装饰器的methods参数来处理不同的HTTP方法

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

The example above keeps all methods for the route within one function, which can be useful if each part uses some common data.

上面的示例将路由的所有方法保留在一个函数中,如果每个部分都使用一些通用数据,这会很有用

You can also separate views for different methods into different functions. Flask provides a shortcut for decorating such routes with get(), post(), etc. for each common HTTP method.

您还可以将不同方法的视图分成不同的函数。 Flask 为每个常见的 HTTP 方法提供了使用 get()、post() 等修饰此类路由的快捷方式。

@app.get('/login')
def login_get():
    return show_the_login_form()

@app.post('/login')
def login_post():
    return do_the_login()

If GET is present, Flask automatically adds support for the HEAD method and handles HEAD requests according to the HTTP RFC. Likewise, OPTIONS is automatically implemented for you

如果存在 GET,Flask 会自动添加对 HEAD 方法的支持,并根据 HTTP RFC 处理 HEAD 请求。同样,OPTIONS 会自动为您实现

3.6 Static Files(静态资源)

Dynamic web applications also need static files. That’s usually where the CSS and JavaScript files are coming from. Ideally your web server is configured to serve them for you, but during development Flask can do that as well. Just create a folder called static in your package or next to your module and it will be available at /static on the application.

动态 Web 应用程序也需要静态文件。这通常是 CSS 和 JavaScript 文件的来源。理想情况下,您的 Web 服务器配置为为您提供服务,但在开发过程中 Flask 也可以做到这一点。只需在包中或模块旁边创建一个名为 static 的文件夹,它将在应用程序的 /static 中可用

To generate URLs for static files, use the special 'static' endpoint name:

要为静态文件生成 URL,请使用特殊的 "static "端点名称

url_for('static', filename='style.css')

The file has to be stored on the filesystem as static/style.css.

该文件必须作为 static/style.css 存储在文件系统上

3.7 Rendering Templates(模版渲染)

Generating HTML from within Python is not fun, and actually pretty cumbersome because you have to do the HTML escaping on your own to keep the application secure. Because of that Flask configures the Jinja2 template engine for you automatically.

从 Python 中生成 HTML 并不有趣,而且实际上相当麻烦,因为您必须自己进行 HTML 转义以确保应用程序的安全。因此 Flask 会自动为您配置 Jinja2 模板引擎

Templates can be used to generate any type of text file. For web applications, you’ll primarily be generating HTML pages, but you can also generate markdown, plain text for emails, and anything else.

模板可用于生成任何类型的文本文件。对于 Web 应用程序,您主要生成 HTML 页面,但也可以生成 Markdown、电子邮件的纯文本以及其他任何内容

For a reference to HTML, CSS, and other web APIs, use the MDN Web Docs.

有关 HTML、CSS 和其他 Web API 的参考,请使用 MDN Web 文档

To render a template you can use the render_template() method. All you have to do is provide the name of the template and the variables you want to pass to the template engine as keyword arguments. Here’s a simple example of how to render a template:

要渲染模板,您可以使用 render_template() 方法。您所要做的就是提供模板的名称以及要作为关键字参数传递给模板引擎的变量。这是如何渲染模板的简单示例

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

Flask will look for templates in the templates folder. So if your application is a module, this folder is next to that module, if it’s a package it’s actually inside your package:

Flask 将在 templates 文件夹中查找模板。因此,如果您的应用程序是一个模块,则此文件夹位于该模块旁边,如果它是一个包,则它实际上位于您的包内

Case 1: a module:

/application.py
/templates
    /hello.html

Case 2: a package:

/application
    /__init__.py
    /templates
        /hello.html

For templates you can use the full power of Jinja2 templates. Head over to the official Jinja2 Template Documentation for more information.

对于模板,您可以使用 Jinja2 模板的全部功能。前往官方 Jinja2 模板文档了解更多信息

Here is an example template:

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

Inside templates you also have access to the config, request, session and g [1] objects as well as the url_for() and get_flashed_messages() functions.

在模板内部,您还可以访问 config、request、session 和 g [1] 对象以及 url_for() 和 get_flashed_messages() 函数

Templates are especially useful if inheritance is used. If you want to know how that works, see Template Inheritance. Basically template inheritance makes it possible to keep certain elements on each page (like header, navigation and footer).

如果使用继承,模板特别有用。如果您想了解其工作原理,请参阅模板继承。基本上,模板继承使得可以在每个页面上保留某些元素(如页眉、导航和页脚)

Automatic escaping is enabled, so if name contains HTML it will be escaped automatically. If you can trust a variable and you know that it will be safe HTML (for example because it came from a module that converts wiki markup to HTML) you can mark it as safe by using the Markup class or by using the |safe filter in the template. Head over to the Jinja 2 documentation for more examples.

自动转义已启用,因此如果名称包含 HTML,它将自动转义。如果您可以信任某个变量并且知道它将是安全的 HTML(例如,因为它来自将 wiki 标记转换为 HTML 的模块),您可以使用 Markup 类或使用 |safe 过滤器将其标记为安全模板。请访问 Jinja 2 文档以获取更多示例

Here is a basic introduction to how the Markup class works:

>>> from markupsafe import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup('<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup('&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
'Marked up » HTML'

3.8 Accessing Request Data(访问请求数据)

For web applications it’s crucial to react to the data a client sends to the server. In Flask this information is provided by the global request object. If you have some experience with Python you might be wondering how that object can be global and how Flask manages to still be threadsafe. The answer is context locals:

对于 Web 应用程序来说,对客户端发送到服务器的数据做出反应至关重要。在 Flask 中,此信息由全局请求对象提供。如果您有一些使用 Python 的经验,您可能想知道该对象如何是全局的以及 Flask 如何仍然保持线程安全。答案是上下文局部变量

3.8.1 The Request Object(请求对象)

The request object is documented in the API section and we will not cover it here in detail (see Request). Here is a broad overview of some of the most common operations. First of all you have to import it from the flask module

请求对象在 API 部分有详细说明,这里不再详述(请参阅 "请求")。以下是一些最常见操作的概述。首先,你必须从 flask 模块中导入它:

from flask import request

The current request method is available by using the method attribute. To access form data (data transmitted in a POST or PUT request) you can use the form attribute. Here is a full example of the two attributes mentioned above:

通过使用method属性可以获取当前的请求方法。要访问表单数据(在 POST 或 PUT 请求中传输的数据),您可以使用 form 属性。这是上面提到的两个属性的完整示例

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

What happens if the key does not exist in the form attribute? In that case a special KeyError is raised. You can catch it like a standard KeyError but if you don’t do that, a HTTP 400 Bad Request error page is shown instead. So for many situations you don’t have to deal with that problem.

如果表单属性中不存在该键会发生什么情况?在这种情况下,会引发特殊的 KeyError。您可以像标准 KeyError 一样捕获它,但如果您不这样做,则会显示 HTTP 400 Bad Request 错误页面。所以在很多情况下你不必处理这个问题

To access parameters submitted in the URL (?key=value) you can use the args attribute:

要访问 URL 中提交的参数 (?key=value),您可以使用 args 属性:

searchword = request.args.get('key', '')

We recommend accessing URL parameters with get or by catching the KeyError because users might change the URL and presenting them a 400 bad request page in that case is not user friendly.

我们建议使用 get 或捕获 KeyError 来访问 URL 参数,因为用户可能会更改 URL 并向他们显示 400 错误请求页面,在这种情况下对用户不友好。

For a full list of methods and attributes of the request object, head over to the Request documentation

有关请求对象的方法和属性的完整列表,请访问请求文档。

3.8.2 File Uploads(上传文件)

You can handle uploaded files with Flask easily. Just make sure not to forget to set the enctype="multipart/form-data" attribute on your HTML form, otherwise the browser will not transmit your files at all.

您可以使用 Flask 轻松处理上传的文件。请确保不要忘记在 HTML 表单上设置 enctype="multipart/form-data" 属性,否则浏览器根本不会传输您的文件

Uploaded files are stored in memory or at a temporary location on the filesystem. You can access those files by looking at the files attribute on the request object. Each uploaded file is stored in that dictionary. It behaves just like a standard Python file object, but it also has a save() method that allows you to store that file on the filesystem of the server. Here is a simple example showing how that works:

上传的文件存储在内存中或文件系统上的临时位置。您可以通过查看请求对象上的文件属性来访问这些文件。每个上传的文件都存储在该字典中。它的行为就像一个标准的 Python 文件对象,但它还有一个 save() 方法,允许您将该文件存储在服务器的文件系统上。这是一个简单的例子,展示了它是如何工作的

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

If you want to know how the file was named on the client before it was uploaded to your application, you can access the filename attribute. However please keep in mind that this value can be forged so never ever trust that value. If you want to use the filename of the client to store the file on the server, pass it through the secure_filename() function that Werkzeug provides for you:

如果您想知道文件在上传到应用程序之前在客户端上是如何命名的,您可以访问 filename 属性。但请记住,该值可以伪造,因此永远不要相信该值。如果您想使用客户端的文件名将文件存储在服务器上,请通过 Werkzeug 为您提供的 secure_filename() 函数传递它

from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['the_file']
        file.save(f"/var/www/uploads/{secure_filename(file.filename)}")
    ...

For some better examples, see Uploading Files.

3.8.3 Cookies

To access cookies you can use the cookies attribute. To set cookies you can use the set_cookie method of response objects. The cookies attribute of request objects is a dictionary with all the cookies the client transmits. If you want to use sessions, do not use the cookies directly but instead use the Sessions in Flask that add some security on top of cookies for you.

要访问 cookie,您可以使用 cookies 属性。要设置 cookie,您可以使用响应对象的 set_cookie 方法。请求对象的 cookies 属性是一个字典,其中包含客户端传输的所有 cookie。如果您想使用会话,请不要直接使用 cookie,而是使用 Flask 中的会话,它会在 cookie 之上为您添加一些安全性

Reading cookies:读cookie

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

Storing cookies:存cookie

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

Note that cookies are set on response objects. Since you normally just return strings from the view functions Flask will convert them into response objects for you. If you explicitly want to do that you can use the make_response() function and then modify it.

请注意,cookie 是在响应对象上设置的。由于您通常只是从视图函数返回字符串,所以 Flask 会为您将它们转换为响应对象。如果您明确想要这样做,可以使用 make_response() 函数,然后对其进行修改。

Sometimes you might want to set a cookie at a point where the response object does not exist yet. This is possible by utilizing the Deferred Request Callbacks pattern.

有时您可能想在响应对象尚不存在的位置设置 cookie。这可以通过利用延迟请求回调模式来实现

For this also see About Responses.

3.9 Redirects and Errors(重定向和错误)

To redirect a user to another endpoint, use the redirect() function; to abort a request early with an error code, use the abort() function:

要将用户重定向到另一个端点,请使用redirect()函数;要使用错误代码提前中止请求,请使用 abort() 函数

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

This is a rather pointless example because a user will be redirected from the index to a page they cannot access (401 means access denied) but it shows how that works.

这是一个毫无意义的示例,因为用户将从索引重定向到他们无法访问的页面(401 表示访问被拒绝),但它显示了它是如何工作的

By default a black and white error page is shown for each error code. If you want to customize the error page, you can use the errorhandler() decorator:

默认情况下,每个错误代码都会显示一个黑白错误页面。如果你想自定义错误页面,可以使用 errorhandler() 装饰器

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

Note the 404 after the render_template() call. This tells Flask that the status code of that page should be 404 which means not found. By default 200 is assumed which translates to: all went well.

请注意 render_template() 调用后的 404。这告诉 Flask 该页面的状态代码应该是 404,这意味着未找到。默认情况下假定为 200,这意味着:一切顺利。

See Handling Application Errors for more details.

3.10 About Responses(响应)

The return value from a view function is automatically converted into a response object for you. If the return value is a string it’s converted into a response object with the string as response body, a 200 OK status code and a text/html mimetype. If the return value is a dict or list, jsonify() is called to produce a response. The logic that Flask applies to converting return values into response objects is as follows:

视图函数的返回值会自动转换为响应对象。如果返回值是字符串,则会将其转换为响应对象,并以字符串作为响应正文、200 OK 状态代码和 text/html mimetype。如果返回值是字典或列表,则调用 jsonify() 来生成响应。 Flask将返回值转换为响应对象的逻辑如下

  1. If a response object of the correct type is returned it’s directly returned from the view.(如果返回正确类型的响应对象,它将直接从视图返回)
  2. If it’s a string, a response object is created with that data and the default parameters.(如果它是字符串,则会使用该数据和默认参数创建响应对象。)
  3. If it’s an iterator or generator returning strings or bytes, it is treated as a streaming response.(如果它是返回字符串或字节的迭代器或生成器,则将其视为流响应)
  4. If it’s a dict or list, a response object is created using jsonify().(如果它是字典或列表,则使用 jsonify() 创建响应对象)
  5. If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form (response, status), (response, headers), or (response, status, headers). The status value will override the status code and headers can be a list or dictionary of additional header values.(如果返回元组,则元组中的项目可以提供额外的信息。此类元组必须采用(响应,状态),(响应,标头)或(响应,状态,标头)的形式。状态值将覆盖状态代码,标头可以是附加标头值的列表或字典)
  6. If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object.(如果这些都不起作用,Flask 将假定返回值是有效的 WSGI 应用程序并将其转换为响应对象)

If you want to get hold of the resulting response object inside the view you can use the make_response() function.

如果您想在视图中获取生成的响应对象,可以使用 make_response() 函数

Imagine you have a view like this:

想象一下你有这样的景色

from flask import render_template

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

You just need to wrap the return expression with make_response() and get the response object to modify it, then return it:

您只需要用 make_response() 包装返回表达式并获取响应对象来修改它,然后返回它

from flask import make_response

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

3.10.1 APIs with JSON

A common response format when writing an API is JSON. It’s easy to get started writing such an API with Flask. If you return a dict or list from a view, it will be converted to a JSON response.

编写 API 时常见的响应格式是 JSON。使用 Flask 编写这样的 API 很容易开始。如果从视图返回字典或列表,它将转换为 JSON 响应

@app.route("/me")
def me_api():
    user = get_current_user()
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }

@app.route("/users")
def users_api():
    users = get_all_users()
    return [user.to_json() for user in users]

This is a shortcut to passing the data to the jsonify() function, which will serialize any supported JSON data type. That means that all the data in the dict or list must be JSON serializable.

这是将数据传递给 jsonify() 函数的快捷方式,该函数将序列化任何支持的 JSON 数据类型。这意味着字典或列表中的所有数据都必须是 JSON 可序列化的。

For complex types such as database models, you’ll want to use a serialization library to convert the data to valid JSON types first. There are many serialization libraries and Flask API extensions maintained by the community that support more complex applications.对于数据库模型等复杂类型,您需要首先使用序列化库将数据转换为有效的 JSON 类型。社区维护了许多序列化库和 Flask API 扩展,支持更复杂的应用程序

3.11 Sessions

In addition to the request object there is also a second object called session which allows you to store information specific to a user from one request to the next. This is implemented on top of cookies for you and signs the cookies cryptographically. What this means is that the user could look at the contents of your cookie but not modify it, unless they know the secret key used for signing.

除了请求对象之外,还有第二个对象,称为会话,它允许您存储从一个请求到下一个请求的特定于用户的信息。这是在 cookie 之上为您实现的,并以加密方式签署 cookie。这意味着用户可以查看 cookie 的内容,但不能修改它,除非他们知道用于签名的密钥。

In order to use sessions you have to set a secret key. Here is how sessions work:

为了使用会话,您必须设置一个密钥。以下是会话的工作原理

from flask import session

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {session["username"]}'
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

How to generate good secret keys

如何生成好的密钥

A secret key should be as random as possible. Your operating system has ways to generate pretty random data based on a cryptographic random generator. Use the following command to quickly generate a value for Flask.secret_key (or SECRET_KEY):

密钥应该尽可能随机。您的操作系统有多种方法可以基于加密随机生成器生成相当随机的数据。使用以下命令快速生成 Flask.secret_key (或 SECRET_KEY)的值:

$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

A note on cookie-based sessions: Flask will take the values you put into the session object and serialize them into a cookie. If you are finding some values do not persist across requests, cookies are indeed enabled, and you are not getting a clear error message, check the size of the cookie in your page responses compared to the size supported by web browsers.

关于基于 cookie 的会话的注释:Flask 将获取您放入会话对象中的值并将它们序列化到 cookie 中。如果您发现某些值在请求中不持久,则 cookie 确实已启用,并且您没有收到明确的错误消息,请检查页面响应中 cookie 的大小与 Web 浏览器支持的大小。

Besides the default client-side based sessions, if you want to handle sessions on the server-side instead, there are several Flask extensions that support this.

除了默认的基于客户端的会话之外,如果您想在服务器端处理会话,还有几个 Flask 扩展支持此功能

3.12 Message Flashing(闪现)

Good applications and user interfaces are all about feedback. If the user does not get enough feedback they will probably end up hating the application. Flask provides a really simple way to give feedback to a user with the flashing system. The flashing system basically makes it possible to record a message at the end of a request and access it on the next (and only the next) request. This is usually combined with a layout template to expose the message.

好的应用程序和用户界面都与反馈有关。如果用户没有得到足够的反馈,他们可能最终会讨厌该应用程序。 Flask 提供了一种非常简单的方法来通过Flashing向用户提供反馈。Flashing基本上可以在请求结束时记录消息并在下一个(且仅下一个)请求时访问它。这通常与布局模板结合以公开消息。

To flash a message use the flash() method, to get hold of the messages you can use get_flashed_messages() which is also available in the templates. See Message Flashing for a full example.

要Flashing,请使用 flash() 方法,要获取消息,您可以使用 get_flashed_messages(),该方法也可在模板中使用。有关完整示例,请参阅消息闪烁

3.13 Logging(日志)

Sometimes you might be in a situation where you deal with data that should be correct, but actually is not. For example you may have some client-side code that sends an HTTP request to the server but it’s obviously malformed. This might be caused by a user tampering with the data, or the client code failing. Most of the time it’s okay to reply with 400 Bad Request in that situation, but sometimes that won’t do and the code has to continue working.

有时,您可能会遇到这样的情况:您处理的数据应该是正确的,但实际上并不正确。例如,您可能有一些客户端代码向服务器发送 HTTP 请求,但它的格式显然是错误的。这可能是由于用户篡改数据或客户端代码失败造成的。大多数情况下,在这种情况下回复 400 Bad Request 是可以的,但有时这样做不行,代码必须继续工作

You may still want to log that something fishy happened. This is where loggers come in handy. As of Flask 0.3 a logger is preconfigured for you to use.

您可能仍然想记录发生了可疑的事情。这就是记录器派上用场的地方。从 Flask 0.3 开始,已预先配置了一个记录器供您使用

Here are some example log calls:

以下是一些日志调用示例

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

The attached logger is a standard logging Logger, so head over to the official logging docs for more information.

附加的记录器是标准日志记录器,因此请参阅官方日志记录文档以获取更多信息。

See Handling Application Errors.

3.14 Hooking in WSGI Middleware(wsgi中间件)

To add WSGI middleware to your Flask application, wrap the application’s wsgi_app attribute. For example, to apply Werkzeug’s ProxyFix middleware for running behind Nginx:

要将 WSGI 中间件添加到 Flask 应用程序,请包装应用程序的 wsgi_app 属性。例如,应用 Werkzeug 的 ProxyFix 中间件在 Nginx 后面运行

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)

Wrapping app.wsgi_app instead of app means that app still points at your Flask application, not at the middleware, so you can continue to use and configure app directly.

包装app.wsgi_app而不是app意味着app仍然指向您的Flask应用程序,而不是中间件,因此您可以继续直接使用和配置app

3.15 Using Flask Extensions(使用Flask扩展)

Extensions are packages that help you accomplish common tasks. For example, Flask-SQLAlchemy provides SQLAlchemy support that makes it simple and easy to use with Flask.

扩展是帮助您完成常见任务的包。例如,Flask-SQLAlchemy 提供 SQLAlchemy 支持,使其与 Flask 一起使用变得简单易用。

For more on Flask extensions, see Extensions.

有关 Flask 扩展的更多信息,请参阅扩展。

3.16 Deploying to a Web Server(发布)

Ready to deploy your new Flask app? See Deploying to Production.

准备好部署新的 Flask 应用程序了吗?请参阅部署到生产。

posted @ 2023-11-13 22:39  刘清政  阅读(386)  评论(0编辑  收藏  举报