)
Flask Quickstart从零到生产的完整实践指南最小应用、路由、模板、请求、会话与部署【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flask本篇技术指南以 Flask 官方文档的 Quickstart 为核心骨架完整覆盖从「最小可运行应用」到「调试、路由、模板渲染、请求数据处理、响应机制、会话、日志与 WSGI 中间件」的全部入门要素并结合当前仓库src/flask/下的源码实现逐节验证文档行为。读完本篇你可以独立完成一个可运行、可调试、可部署的 Flask 应用的编写并理解每个 API 背后的真实调用链。一、最小应用四行代码理解 WSGI 应用骨架一个最小 Flask 应用如下保存为hello.pyfrom flask import Flask app Flask(__name__) app.route(/) def hello_world(): return pHello, World!/p这四行代码完成了四件事导入Flask类。它的实例就是 WSGI 应用程序本身。Flask类在 Flask 包的公共 API 入口 中导出该文件同时导出了Blueprint、request、session、url_for、render_template、abort、redirect、jsonify等几乎所有入门文档用到的对象。创建实例第一个参数是模块或包名。__name__是大多数场景下的便捷写法。Flask 需要它来定位资源查找的基准路径——模板templates文件夹和静态文件static文件夹都是相对于这个路径解析的。用app.route装饰器告诉 Flask 哪个 URL 触发哪个函数。从源码看route定义在 Scaffold.route其内部装饰器只做一件事取出endpoint默认为视图函数名后调用add_url_rule完成注册methods默认值为[GET]HEAD和OPTIONS会被自动补充。视图函数返回要展示的 HTML。默认内容类型是 HTML字符串中的 HTML 会被浏览器直接渲染。注意千万不要把应用文件命名为flask.py否则会遮蔽 Flask 库本身导致导入冲突。二、启动开发服务器flask run与端口、网络地址问题运行应用使用flask命令或python -m flask必须通过--app选项告诉 Flask 应用在哪$ flask --app hello run * Serving Flask app hello * Running on http://127.0.0.1:5000 (Press CTRLC to quit)应用发现行为Application Discovery如果文件恰好命名为app.py或wsgi.py可以省略--app选项。完整发现规则见 CLI 文档。仓库中的 cliapp 测试应用 和 factory 模式示例 展示了这两种约定在真实项目中的形态。这个内置服务器足够测试使用但生产部署应使用 部署指南 中的方案如 Gunicorn、Waitress 等。端口被占用如果 5000 端口已被其他程序占用服务器启动时会报OSError: [Errno 98]Linux或OSError: [WinError 10013]Windows处理办法见 服务器文档。对外可见的服务器默认情况下服务器只能从本机访问。这是有意为之——调试模式下网络上的用户可以执行任意 Python 代码。如果已禁用调试器或信任局域网用户可以加上--host0.0.0.0让操作系统监听所有网络接口$ flask run --host0.0.0.0三、调试模式自动重载与浏览器内交互式调试器flask run不仅能启动开发服务器启用调试模式后代码变更会自动重载服务请求中发生错误时会在浏览器里展示交互式调试器见文首截图。安全警告调试器允许从浏览器执行任意 Python 代码。虽然受 PIN 码保护但仍是重大安全风险绝不要在生产环境运行开发服务器或调试器。启用方式$ flask --app hello run --debug * Serving Flask app hello * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRLC to quit) * Restarting with stat * Debugger is active! * Debugger PIN: nnn-nnn-nnn相关延伸阅读服务器运行文档、CLI 文档、内置调试器与其他调试器、日志与错误处理 和 错误处理。四、HTML 转义手动防护注入攻击返回 HTMLFlask 的默认响应类型时任何用户提供的值都必须转义以防御注入攻击。后续引入的 Jinja 模板会自动转义在纯 Python 中返回 HTML 时可以用markupsafe.escape手动转义from flask import request from markupsafe import escape app.route(/hello) def hello(): name request.args.get(name, Flask) return fHello, {escape(name)}!如果用户提交/hello?namescriptalert(bad)/script转义会使其按纯文本渲染而不是在用户浏览器中执行脚本。示例代码里为了简洁常省略转义但你必须时刻清楚不可信数据的使用方式。五、路由规则、转换器、尾斜杠与 URL 反向解析5.1 基础路由用app.route把函数绑定到 URLapp.route(/) def index(): return Index Page app.route(/hello) def hello(): return Hello, World一个函数可以挂多条规则URL 中还可以包含变量段。5.2 变量规则与类型转换器用variable_name标记 URL 的动态段函数会收到同名关键字参数可选地用converter:variable_name指定类型from markupsafe import escape app.route(/user/username) def show_user_profile(username): # show the user profile for that user return fUser {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 fPost {post_id} app.route(/path/path:subpath) def show_subpath(subpath): # show the subpath after /path/ return fSubpath {escape(subpath)}转换器一览转换器说明string默认接受不含斜杠的任意文本int接受正整数float接受正浮点数path与string相同但接受斜杠uuid接受 UUID 字符串从源码结构看转换器挂在app.url_map.converters上SansIOMixin.url_map 的文档 明确支持创建类后注入自定义转换器例如app.url_map.converters[list] ListConverter。转换器测试 覆盖了自定义转换器的注册与匹配行为。5.3 唯一 URL 与尾斜杠重定向行为以下两条规则的区别在于尾斜杠app.route(/projects/) def projects(): return The project page app.route(/about) def about(): return The about pageprojects端点的规范 URL带尾斜杠类似文件系统的文件夹访问/projects无斜杠时Flask 会重定向到规范地址/projects/。about端点的规范 URL不带尾斜杠类似文件路径访问/about/带斜杠会 404。这种设计保证同一资源只有一个规范 URL避免搜索引擎把同一个页面索引两次。5.4 URL 反向解析url_for用 flask.url_for 构建指向某函数的 URL第一个参数是函数名端点名后续关键字参数对应规则中的变量段多余的未知变量会作为查询参数追加到 URL 尾部。为什么用反向解析而不在模板中硬编码 URL反向解析通常比硬编码更具描述性改 URL 时一处修改即可不必满模板找硬编码地址自动处理特殊字符的转义生成的路径永远是绝对路径避免浏览器相对路径的意外行为应用挂载在 URL 根之外如/myapplication时url_for会正确带上前缀。用app.test_request_context可以在 Python shell 中模拟「正在处理请求」来试用url_for其原理见 应用上下文文档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, usernameJohn Doe))输出/ /login /login?next/ /user/John%20Doe注意最后一条空格被自动转义为%20验证了上面第 3 条。5.5 HTTP 方法默认路由只响应GET。用route的methods参数处理多种方法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()上面的写法把所有方法集中在一个函数里适合各分支共享公共数据的场景。也可以把不同方法拆到不同函数Flask 2.0 起为每种常用方法提供快捷装饰器app.get(/login) def login_get(): return show_the_login_form() app.post(/login) def login_post(): return do_the_login()源码佐证Scaffold.get / Scaffold.post 等快捷方法都通过_method_route转调self.route(rule, methods[method])并且会直接拒绝methods参数TypeError(Use the route decorator to use the methods argument.)防止语义混淆。若路由包含GETFlask 会自动支持HEAD方法并按 HTTP RFC 处理OPTIONS也会被自动实现。六、静态文件static文件夹与static端点动态应用需要 CSS、JavaScript 等静态文件。生产环境理想情况下由 Web 服务器负责开发期间 Flask 可以直接服务——只需在包内或模块旁创建名为static的文件夹应用内即可通过/static访问。生成静态文件 URL 使用特殊的static端点名url_for(static, filenamestyle.css)文件必须存放为static/style.css。从源码看这一机制在 Flask.init中自动完成只要has_static_folder为真构造函数参数static_folder默认值为static就会注册一条f{self.static_url_path}/path:filename规则、端点名固定为static、视图调用send_static_file。因此static_url_path、static_folder都可以按需在构造函数中改写。七、模板渲染Jinja 配置、目录约定与自动转义在 Python 中手工拼接 HTML 既痛苦又不安全转义必须自己做因此 Flask 自动为你配置好 Jinja 模板引擎。模板可以生成任意文本文件——网页应用里主要是 HTML但 Markdown、邮件纯文本等都可以生成。渲染模板用render_template传入模板名模板变量作为关键字参数from flask import render_template app.route(/hello/) app.route(/hello/name) def hello(nameNone): return render_template(hello.html, personname)Flask 在templates文件夹中查找模板。若是模块文件夹在模块旁边若是包文件夹在包内部情况 1模块/application.py /templates /hello.html情况 2包/application /__init__.py /templates /hello.html完整 Jinja 语法见 Jinja 官方模板文档。一个示例模板!doctype html titleHello from Flask/title {% if person %} h1Hello {{ person }}!/h1 {% else %} h1Hello, World!/h1 {% endif %}模板内还可直接使用config、request、session、g对象以及url_for、get_flashed_messages函数。不知道g是什么它是用于存储请求期间你自己需要的信息的对象参考 flask.g 文档 与 SQLite 模式示例。自动转义默认开启若person含 HTML会被自动转义。如果你信任某个变量且确定它是安全 HTML例如来自把 wiki 标记转为 HTML 的模块可以用markupsafe.Markup类或模板中的|safe过滤器标记为安全。Markup的用法 from markupsafe import Markup Markup(strongHello %s!/strong) % blinkhacker/blink Markup(strongHello lt;blinkgt;hackerlt;/blinkgt;!/strong) Markup.escape(blinkhacker/blink) Markup(lt;blinkgt;hackerlt;/blinkgt;) Markup(emMarked up/em raquo; HTML).striptags() Marked up » HTML版本说明自 0.5 起自动转义不再对所有模板生效仅以下扩展名触发.html、.htm、.xml、.xhtml从字符串加载的模板自动转义是关闭的。模板继承让头部、导航、页脚等元素在每页复用见 模板继承模式。八、访问请求数据request代理、表单、查询参数、上传与 Cookie8.1request为什么是“全局”的from flask import request你可能会问Flask 同时处理多个请求request怎么会是全局的答案是它是一个代理对象指向当前工作线程正在处理的那个请求由 Flask 和 Python 的上下文机制内部管理。详见 应用上下文文档。当前请求方法在request.method中。访问表单数据POST/PUT提交的数据用request.form它像字典一样app.route(/login, methods[GET, POST]) def login(): error None if request.method POST: if valid_login(request.form[username], request.form[password]): return store_login(request.form[username]) else: error Invalid username or password # Executed if the request method was GET or the credentials were invalid. return render_template(login.html, errorerror)若form中不存在该键会抛出一个特殊的KeyError你可以像普通KeyError一样捕获它若不捕获Flask 会返回 HTTP 400 Bad Request 错误页。也可以用MultiDict.get取默认值避免报错。访问 URL 中的查询参数?keyvalue用request.args缺键行为与form相同searchword request.args.get(key, )完整属性列表见Request类文档其实现位于 flask.wrappers.Request。8.2 文件上传处理上传文件要注意两点HTML 表单必须设置enctypemultipart/form-data否则浏览器根本不会传文件上传的文件先存于内存或文件系统临时位置通过request.files访问。每个上传文件像标准 Python file 对象还多了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) ...f.filename是客户端上传前的文件名但该值可以被伪造绝不能直接信任。若要据此在服务器落盘必须经过 Werkzeug 提供的secure_filenamefrom 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)}) ...更完善的上传实践见 文件上传模式文档。8.3 读写 Cookie读 Cookie 用request.cookies客户端传来的全部 Cookie 组成的字典写 Cookie 用响应对象的set_cookie方法。若需要会话不要直接操作 Cookie而是用下一节的 Flask 会话它在 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.写from flask import make_response app.route(/) def index(): resp make_response(render_template(...)) resp.set_cookie(username, the username) return resp注意Cookie 设置在响应对象上。视图函数通常只返回字符串Flask 会替你转成响应对象需要显式修改时用make_response先拿到响应对象再改。如果想在响应对象尚不存在的时机设置 Cookie可以借助 延迟回调模式。九、重定向与错误用flask.redirect重定向到其他端点用flask.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()这个例子本身没什么意义用户会被重定向到一个 401 拒绝访问的页面但它演示了机制abort会立即中断后续代码。每个错误码默认展示一个黑白错误页。自定义错误页用app.errorhandler装饰器from flask import render_template app.errorhandler(404) def page_not_found(error): return render_template(page_not_found.html), 404注意render_template后面的404它告诉 Flask 该页状态码为 404未找到若不指定默认 200含义是“一切正常”。更多细节见 错误处理文档。十、深入响应机制返回值的六种转换规则与make_response视图函数的返回值会被自动转换成响应对象。字符串 → 以它为响应体、状态码200 OK、MIME 类型text/html的响应字典或列表 → 调用jsonify生成 JSON 响应。完整转换规则若返回的是正确类型的响应对象直接原样返回若是字符串用该数据和默认参数创建响应对象若是返回str或bytes的迭代器/生成器作为流式响应处理若是字典或列表用flask.jsonify创建响应对象若是元组元组成员可提供额外信息形式必须是(response, status)、(response, headers)或(response, status, headers)。status覆盖默认状态码headers可以是附加头部值的列表或字典以上都不符合时Flask 假设返回值是一个合法的 WSGI 应用并调用它生成响应。这条规则在 Flask.make_response 中实现源码先对元组按长度拆包3 元组直接解包2 元组靠第二个元素是否为Headers/dict/tuple/list判断是头部还是状态码其他长度直接TypeError随后按类型逐一转换。文档同时记录了版本演进2.2 起生成器转为流式响应、列表转为 JSON 响应1.1 起字典转为 JSON 响应。若想在视图内部拿到响应对象做修改用flask.make_response。例如给 404 页加自定义响应头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 resp10.1 JSON API写 API 时常见响应格式是 JSON。从视图返回dict或list即自动转为 JSON 响应app.route(/me) def me_api(): user get_current_user() return { username: user.username, theme: user.theme, image: url_for(user_image, filenameuser.image), } app.route(/users) def users_api(): users get_all_users() return [user.to_json() for user in users]这是把数据传给 flask.jsonify 的快捷方式它会序列化所有受支持的 JSON 数据类型——也就是说 dict/list 里的所有数据必须可 JSON 序列化。对数据库模型这类复杂类型建议先用序列化库或社区维护的 Flask API 扩展转换为合法 JSON 类型。十一、会话Sessions基于签名 Cookie 的跨请求状态除了request还有第二个对象flask.session用于在相邻请求之间保存与某用户相关的信息。它建立在 Cookie 之上并对 Cookie 做密码学签名用户可以查看 Cookie 内容但除非知道签名用的密钥否则无法篡改。会话实现见 flask.sessions。使用前提设置密钥。from flask import session # Set the secret key to some random bytes. Keep this really secret! app.secret_key b_5#y2LF4Q8z\n\xec]/ app.route(/) def index(): if username in session: return fLogged 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 methodpost pinput typetext nameusername pinput typesubmit valueLogin /form app.route(/logout) def logout(): # remove the username from the session if its there session.pop(username, None) return redirect(url_for(index))如何生成好的密钥密钥应尽可能随机操作系统有基于密码学随机数生成器产生随机数据的方式。快速生成Flask.secret_key或配置项SECRET_KEY$ python -c import secrets; print(secrets.token_hex()) 192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbfCookie 大小提示Flask 会把写入 session 的值序列化进 Cookie。如果发现某些值跨请求不持久、Cookie 明明已启用、又没有任何明确报错请检查页面响应中 Cookie 的大小是否超过了浏览器支持的尺寸。默认会话基于客户端。若希望改为服务端管理会话存在多个支持该需求的 Flask 扩展。十二、消息闪现Flashing、日志与 WSGI 中间件12.1 消息闪现好的应用靠反馈取胜。Flask 的 flashing 系统允许在请求末尾记录一条消息并只在**下一个且仅下一个**请求中取出通常配合布局模板展示。发条消息用flask.flash取出消息用flask.get_flashed_messages模板内也可用。完整示例见 Flashing 模式文档。12.2 日志处理到“本应正确但实际错误”的数据客户端篡改、客户端代码故障等时有时不能只回400 Bad Request代码还得继续跑但值得留下记录。自 Flask 0.3 起Flask 为你预配置好了 loggerapp.logger.debug(A value for debugging) app.logger.warning(A warning occurred (%d apples), 42) app.logger.error(An error occurred)app.logger是标准库logging的Logger用法见官方 logging 文档更完整的配置见 日志文档 与 错误处理文档。12.3 接入 WSGI 中间件给 Flask 应用加 WSGI 中间件包装应用的wsgi_app属性即可。例如在 Nginx 后面运行时应用 Werkzeug 的ProxyFix中间件以正确解析X-Forwarded-*头场景说明见 Proxy Fix 部署文档from werkzeug.middleware.proxy_fix import ProxyFix app.wsgi_app ProxyFix(app.wsgi_app)包装app.wsgi_app而非app本身意味着app仍然指向你的 Flask 应用而不是中间件后续可以照常使用和配置app。十三、下一步扩展与部署扩展扩展是帮你完成常见任务的包例如 Flask-SQLAlchemy 提供与 Flask 无缝配合的 SQLAlchemy 支持。更多见 扩展文档。部署内置开发服务器不适用于生产。准备把新应用上线时参考 部署指南其中覆盖了 Gunicorn、uWSGI、Waitress、Nginx 等方案。安装以上所有示例均以按 安装文档 完成项目环境搭建并安装 Flask 为前提。【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flask创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考