一、路由概念解析
路由 (Routing) 是 Web 框架的核心机制,它的作用是将 客户端请求的 URL 映射到 服务器端的处理代码(视图函数)。
你可以把路由想象成公司的前台接待员 或交通指挥中心:
- URL 是客人的目的地地址(例如:"我要去财务部")。
- 路由表 是公司的楼层索引图。
- 视图函数 是具体的部门(财务部),负责处理客人的具体需求。
- 路由系统 就是那个接待员,它查看索引图,把客人引导到正确的部门。
如果没有路由,Web 服务器就不知道当用户访问 http://example.com/user/123 时,该执行哪段代码来显示用户 123 的信息。
1. 路由的核心三要素
在 Flask(以及大多数 Web 框架)中,一个完整的路由定义通常包含三个核心部分:
A. URL 规则 (Rule / Pattern)
这是用户浏览器地址栏里看到的字符串模式。
- 静态部分 :固定不变的文本,如
/users/。 - 动态部分 :可变的部分,用尖括号包裹,如
<user_id>。 - 示例 :
/blog/<int:post_id>/comment/<comment_id>
B. 端点 (Endpoint)
这是路由在内部的唯一标识符(ID)。
- Flask 默认使用视图函数的名称作为端点。
- 它的主要作用是供
url_for()函数使用,用于在代码内部反向生成 URL,而不是硬编码路径。 - 示例 :函数名
show_post就是端点。
C. 视图函数 (View Function / Handler)
这是当 URL 匹配成功时,实际被执行的 Python 函数。
- 它接收从 URL 中提取的参数。
- 它必须返回一个 响应对象(通常是字符串、HTML 或 JSON)。
2. 路由的工作流程 (Request Lifecycle)
当一个请求到达 Flask 应用时,路由系统按以下步骤工作:
- 接收请求 :Werkzeug(Flask 的底层库)接收到 HTTP 请求,提取出
PATH_INFO(即 URL 路径部分,如/user/5)和REQUEST_METHOD(如GET)。 - 匹配规则 :遍历内部的路由映射表(Map),寻找与当前路径和 HTTP 方法相匹配的规则。
- 匹配顺序:通常按照注册顺序,但更具体的规则(如静态路径)通常优于通用规则(如动态参数)。
- 参数提取与转换 :
- 如果规则中有
<int:id>,系统会尝试将 URL 中的对应部分转换为整数。 - 如果转换失败(例如 URL 是
/user/abc但规则要求int),匹配立即失败,继续查找或直接进入 404 流程。
- 如果规则中有
- 分派执行 :一旦找到匹配项,Flask 调用对应的视图函数,并将提取出的参数作为关键字参数传入。
- 返回响应:视图函数执行完毕,返回响应内容给客户端。
- 未匹配处理 :如果遍历完所有规则都没找到匹配项,触发 404 Not Found 错误处理器。如果路径匹配但 HTTP 方法不匹配(例如只定义了 POST 却发了 GET),触发 405 Method Not Allowed。
3. 深度解析:动态路由与转换器
这是路由最强大的地方。它不仅仅是字符串匹配,还包含逻辑验证。
为什么需要转换器?
假设你有一个路由 /user/<id>。
- 如果不加限制,
/user/admin和/user/123都会匹配。 - 但在数据库查询时,你可能期望
id是整数。如果传入 "admin",数据库查询可能会报错或产生安全漏洞(SQL 注入风险)。
Flask 的内置转换器
Flask 通过在尖括号内指定类型来解决这个问题:
<int:id>:只匹配正整数。如果是/user/abc,直接 404,根本不会进入你的函数。这相当于在入口处做了一层数据清洗。<float:price>:匹配浮点数。<path:filename>:匹配包含斜杠的路径(常用于文件下载)。<uuid:token>:匹配标准的 UUID 格式。
本质:转换器是路由匹配过程中的"守门员",只有符合类型的数据才能通过。
4. 关键概念辨析
A. 路由 vs. URL
- URL 是给用户看的地址。
- 路由 是服务器内部的映射规则。
- 一个 URL 对应一个路由规则,但一个路由规则可以匹配无数个 URL(因为有动态参数)。
B. 端点 (Endpoint) vs. 函数名
- 虽然默认一样,但它们概念不同。
- 函数名 是 Python 代码层面的标识。
- 端点 是 Flask 路由表层面的标识。
- 场景:你可以让两个不同的 URL 指向同一个端点(别名),或者让一个 URL 指向一个匿名函数(此时必须显式指定 endpoint)。
C. 正向解析 vs. 反向构建
- 正向解析 (Matching):URL -> 函数。用户访问网址,服务器找代码。
- 反向构建 (Building) :函数/端点 -> URL。代码中调用
url_for('login'),Flask 查表生成/login。 - 最佳实践 :永远优先使用反向构建 。这样如果你将来把
/login改成/sign-in,只需要改路由定义处,所有调用url_for的地方自动更新,无需全局搜索替换。
5. 常见误区与陷阱
1. 斜杠的重要性 (Trailing Slash)
- 目录风格 (
/projects/) :Flask 认为这是一个目录。如果你访问/projects(没斜杠),它会 308 重定向 到/projects/。这是为了 SEO(搜索引擎认为带不带斜杠是两个页面)。 - 文件风格 (
/about) :Flask 认为这是一个文件。如果你访问/about/(有斜杠),它会直接 404,不会重定向。 - 建议:保持团队规范统一。API 通常不带斜杠,传统网页目录带斜杠。
2. 路由顺序
Flask 按照注册顺序匹配路由。
- 错误写法 :先注册通用路由
/user/<name>,再注册具体路由/user/me。- 结果:访问
/user/me时,会被第一条规则捕获,name变成了 "me",第二条规则永远无法生效。
- 结果:访问
- 正确写法 :具体的在前,通用的在后。
3. HTTP 方法隐式支持
- 定义
GET自动支持HEAD。 - 定义任何方法自动支持
OPTIONS。 - 如果你只写了
methods=['POST'],用户用浏览器地址栏访问(默认 GET)会报 405 Method Not Allowed,而不是 404。这点区分很重要:404 是"路不对",405 是"路对但方式不对"。
6. 总结:路由的本质
路由不仅仅是"URL 到函数的映射",它是一个声明式的接口定义层。
- 解耦:它将 URL 结构(前端/用户视角)与代码逻辑(后端/开发者视角)解耦。你可以随时重构 URL 而不破坏内部逻辑,反之亦然。
- 验证:通过转换器,它在代码执行前就完成了初步的数据类型验证。
- 规范 :通过
methods参数,它强制规定了资源的访问方式(RESTful 基础)。 - 可维护性 :通过
endpoint和url_for,它保证了项目重构时的安全性。
二、flask路由
Flask 的路由系统基于 Werkzeug 库(Flask 的底层 WSGI 工具库),它提供了一个强大且灵活的 URL 映射机制。路由不仅仅是将 URL 指向函数,它还负责 URL 规范化、参数解析、类型转换、HTTP 方法分发以及反向 URL 构建。
1. 核心机制:装饰器与视图函数
1.1 基础装饰器 @app.route
这是最常用的方式。装饰器本质上是将函数注册到应用内部的 URL 映射表(Map)中。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "首页"
底层原理 :
当你使用 @app.route 时,Flask 实际上调用了 app.add_url_rule 方法。上述代码等价于:
def index():
return "首页"
app.add_url_rule('/', 'index', index)
# 参数说明: rule (路径), endpoint (端点名称, 默认为函数名), view_func (视图函数)
为什么了解 add_url_rule 很重要?
在某些动态场景下(例如根据配置文件动态生成路由,或在类视图中),直接使用 add_url_rule 比装饰器更灵活。
1.2 端点 (Endpoint) 的重要性
每个路由都有一个唯一的 endpoint(端点)。
-
默认情况下,endpoint 是视图函数的名称。
-
url_for()函数正是通过 endpoint 来查找对应的 URL。 -
最佳实践 :在大型项目中,建议显式指定 endpoint,通常采用
蓝图名.函数名或模块.函数名的命名空间风格,避免命名冲突。@app.route('/user/profile', endpoint='user_profile')
def profile():
pass生成 URL: url_for('user_profile')
2. 动态路由与变量规则 (Variable Rules)
Flask 允许在 URL 中嵌入变量,这些变量会作为关键字参数传递给视图函数。
2.1 转换器 (Converters)
Flask 内置了多种转换器,用于验证和转换 URL 中的参数类型。如果类型不匹配,Flask 会自动返回 404 Not Found。
| 转换器 | 描述 | 示例匹配 | 示例不匹配 |
|---|---|---|---|
string |
默认。不含斜杠 / 的文本 |
<name> |
- |
int |
正整数 | <id> (匹配 123) |
-5, abc |
float |
浮点数 | <price> (匹配 3.14) |
abc |
path |
类似 string,但包含斜杠 | <file_path> (匹配 a/b/c) |
- |
uuid |
UUID 字符串 | <uuid> (匹配 550e8400...) |
非 UUID 格式 |
any |
匹配预定义列表中的任意一个 | <any(a,b,c):item> |
d |
自定义转换器 :
你可以继承 BaseConverter 创建自己的转换器(例如限制只能匹配特定正则表达式)。
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
def __init__(self, url_map, *items):
super(RegexConverter, self).__init__(url_map)
self.regex = items[0]
app.url_map.converters['regex'] = RegexConverter
@app.route('/regex/<regex("[a-z]{3}"):code>')
def match_code(code):
return f"匹配到的代码: {code}"
# 仅匹配三个小写字母,如 /regex/abc,/regex/123 会报 404
2.2 多个动态参数
一个路由可以包含多个变量,顺序必须与函数参数一致(除非使用关键字参数解包,但通常按顺序)。
@app.route('/user/<username>/post/<int:post_id>')
def show_post(username, post_id):
return f"用户 {username} 的第 {post_id} 篇帖子"
3. HTTP 方法处理与方法视图
3.1 指定 Methods
默认只接受 GET。可以通过 methods 列表显式声明。
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 处理登录逻辑
return "Login Success"
# 处理 GET 请求,显示表单
return "Show Login Form"
自动处理 HEAD 和 OPTIONS:
- 如果你定义了
GET,Flask 会自动支持HEAD。 - 如果你定义了其他方法,Flask 会自动支持
OPTIONS(用于 CORS 预检请求)。 - 注意 :不需要显式写出
HEAD或OPTIONS,除非你需要自定义它们的响应逻辑。
3.2 方法分派器 (MethodView)
当一个 URL 需要根据不同的 HTTP 方法执行完全不同的逻辑,且逻辑较复杂时,使用基于类的 MethodView 比在一个函数里写一堆 if request.method == ... 更清晰。
from flask.views import MethodView
from flask import jsonify
class UserAPI(MethodView):
def get(self, user_id=None):
if user_id is None:
return jsonify({"msg": "获取用户列表"})
return jsonify({"msg": f"获取用户 {user_id}"})
def post(self):
return jsonify({"msg": "创建新用户"})
def put(self, user_id):
return jsonify({"msg": f"更新用户 {user_id}"})
def delete(self, user_id):
return jsonify({"msg": f"删除用户 {user_id}"})
# 注册路由
app.add_url_rule('/users/', view_func=UserAPI.as_view('users_api'))
app.add_url_rule('/users/<int:user_id>', view_func=UserAPI.as_view('user_api'))
优点:代码结构清晰,符合 RESTful 设计原则,易于扩展。
4. URL 规范化与重定向行为 (Trailing Slashes)
这是新手最容易混淆的地方,Flask 遵循 RFC 标准 处理斜杠。
4.1 目录风格 (带斜杠 /)
@app.route('/projects/')
def projects():
return "项目列表"
- 访问
/projects/-> 200 OK - 访问
/projects(无斜杠) -> 308 Permanent Redirect 到/projects/ - 原因 :这被视为一个"目录"。搜索引擎认为
/projects和/projects/是两个不同的页面,为了避免重复内容(SEO 问题),Flask 强制重定向到规范 URL(带斜杠)。
4.2 文件风格 (不带斜杠)
@app.route('/about')
def about():
return "关于页面"
- 访问
/about-> 200 OK - 访问
/about/(带斜杠) -> 404 Not Found - 原因 :这被视为一个"文件"。在 Unix 系统中,
file和file/是不同的。如果用户访问/about/,Flask 不会重定向,而是直接报错,因为假设该路径不存在。
建议:保持一致性。如果是资源集合(列表),用斜杠;如果是具体资源或动作,通常不用斜杠(或者全用斜杠,取决于团队规范,但要理解重定向机制)。
5. 高级路由功能
5.1 子域名 (Subdomains)
Flask 支持基于子域名的路由,常用于多租户 SaaS 应用或区分 API 版本。
注意:需要在配置中设置 SERVER_NAME 才能生效(即使在本地开发)。
app.config['SERVER_NAME'] = 'example.com'
@app.route('/', subdomain='admin')
def admin_index():
return "Admin Panel (admin.example.com)"
@app.route('/', subdomain='<tenant_id>')
def tenant_index(tenant_id):
return f"Tenant: {tenant_id} ({tenant_id}.example.com)"
5.2 路由优先级
当定义多个可能匹配同一 URL 的路由时,定义的顺序至关重要。Flask 按照注册顺序进行匹配,一旦匹配成功即停止。
# 错误示例
@app.route('/user/<name>') # 先注册
def show_user(name):
pass
@app.route('/user/me') # 后注册,永远无法被访问到!
def show_me():
pass
# 访问 /user/me 会被第一条路由捕获,name='me'
# 正确示例:具体的路由放在前面
@app.route('/user/me')
def show_me():
pass
@app.route('/user/<name>')
def show_user(name):
pass
5.3 构建 URL (url_for)
永远不要在代码或模板中硬编码 URL 路径。使用 url_for。
from flask import url_for
# 基本用法
url_for('index') # -> '/'
# 带参数
url_for('show_user', username='john') # -> '/user/john'
# 外部链接 (生成绝对 URL)
url_for('index', _external=True) # -> 'http://example.com/'
# 添加锚点
url_for('docs', section='install', _anchor='linux') # -> '/docs#linux'
# 处理特殊字符
url_for('search', q='hello world') # -> '/search?q=hello+world' (自动 URL 编码)
优势:
- 重构安全 :如果修改了路由路径,只需改一处,所有
url_for调用自动更新。 - 测试友好 :在测试环境中可以轻松改变
SERVER_NAME而不影响代码逻辑。 - 国际化:结合蓝图,可以轻松为不同语言前缀生成 URL。
6. 大型应用架构:蓝图 (Blueprints)
当应用变大时,将所有路由写在 app.py 是不可维护的。蓝图 是 Flask 提供的模块化解决方案。
6.1 蓝图的作用
- URL 前缀 :自动为蓝图内所有路由添加前缀(如
/api/v1)。 - 模板/静态文件夹隔离:每个蓝图可以有独立的模板和静态资源目录。
- 注册钩子 :蓝图可以拥有自己的
before_request等钩子,仅作用于该模块。
6.2 实现示例
结构:
/myapp
/auth
__init__.py (定义蓝图)
views.py (定义路由)
/api
__init__.py
views.py
app.py (主程序)
auth/views.py:
from flask import Blueprint, render_template
# 定义蓝图,url_prefix 可选
auth_bp = Blueprint('auth', __name__, url_prefix='/auth', template_folder='templates')
@auth_bp.route('/login')
def login():
# 实际访问路径为 /auth/login
return render_template('auth/login.html')
@auth_bp.route('/register')
def register():
return "Register Page"
from flask import Flask
from auth.views import auth_bp
from api.views import api_bp
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(auth_bp)
app.register_blueprint(api_bp, url_prefix='/api/v1') # 可以在注册时再次覆盖或追加前缀
if __name__ == '__main__':
app.run()
此时,login 的完整路径是 /auth/login。如果在注册时加了 /api/v1,则变成 /api/v1/auth/login(通常不建议嵌套过深,一般在蓝图定义时定好前缀)。
7. 性能优化与调试技巧
7.1 查看路由表
在开发过程中,查看所有已注册的路由非常有用。
with app.app_context():
for rule in app.url_map.iter_rules():
print(f"{rule.methods} {rule.rule} -> {rule.endpoint}")
或者在 Python Shell 中直接打印 app.url_map。
7.2 严格斜杠模式 (STRICT_SLASHES)
可以通过配置 app.strict_slashes = False 来改变默认行为。
- 设置为
False后,无论 URL 是否有斜杠,只要路径匹配即可访问,不再强制重定向。 - 慎用:这可能导致 SEO 问题(重复内容),通常只在内部 API 或特定遗留系统中使用。
7.3 路由匹配性能
- 正则复杂度:虽然 Flask 支持正则转换器,但过于复杂的正则表达式会影响路由匹配速度。尽量使用内置转换器。
- 路由数量:如果有成千上万条路由,Werkzeug 的匹配算法(基于树结构)依然高效,但要注意避免大量的动态前缀冲突。
7.4 404 和 405 错误处理
-
404:URL 没有匹配到任何路由。
-
405 :URL 匹配到了路由,但使用的 HTTP 方法不在允许的
methods列表中。
你可以自定义这些错误处理器:@app.errorhandler(404)
def not_found(e):
return render_template('404.html'), 404@app.errorhandler(405)
def method_not_allowed(e):
return jsonify({"error": "Method not allowed"}), 405
8. 常见陷阱与最佳实践总结
| 问题 | 描述 | 解决方案 |
|---|---|---|
| 循环导入 | 在蓝图中导入 app 对象,或在 app 中导入蓝图视图,导致循环依赖。 |
使用 Application Factory 模式,并在蓝图中使用 current_app 或延迟导入。 |
| 硬编码 URL | 在 HTML 或 JS 中写死 /user/123。 |
始终使用 url_for(),甚至在 JS 中可以通过后端注入 URL 字典。 |
| 路由顺序错误 | 通用动态路由挡住了具体静态路由。 | 具体路由在前,通用动态路由在后。 |
| 忘记 methods | 表单提交 POST 请求却只定义了 GET。 | 显式声明 methods=['GET', 'POST']。 |
| 变量名不匹配 | URL 中是 <user_id>,函数参数是 uid。 |
确保 URL 变量名与函数参数名完全一致。 |
| 全局状态污染 | 在视图函数外定义可变全局变量存储请求数据。 | 永远不要在全局作用域存储请求相关数据,使用 g 对象或上下文局部变量。 |
终极建议
- RESTful 设计 :对于 API,优先使用
MethodView或清晰的methods定义,保持动词在 HTTP 方法中,名词在 URL 中。 - 版本控制 :API 路由务必包含版本号(如
/api/v1/...),以便未来平滑升级。 - 文档化 :配合 Swagger/OpenAPI 工具(如
flasgger或flask-smorest),让路由定义自动生成文档。 - 测试驱动:为每个关键路由编写单元测试,验证不同方法和参数下的行为。
三、flask路由测试
1. 完整测试代码 (test_routes.py)
这个脚本涵盖了:
-
基础路由
-
动态参数与类型转换 (int, string, path)
-
HTTP 方法处理 (GET, POST, PUT)
-
URL 规范化 (斜杠重定向测试)
-
MethodView (类视图)
-
自定义 404/405 错误处理
-
路由列表打印 (启动时自动打印所有注册的路由)
from flask import Flask, request, jsonify, url_for, abort
from flask.views import MethodViewapp = Flask(name)
---------------------------------------------------------
1. 基础路由
---------------------------------------------------------
@app.route('/')
def index():
return "🏠 首页: 欢迎使用 Flask 路由测试系统!
访问 /docs 查看测试指南。"---------------------------------------------------------
2. 动态路由与类型转换
---------------------------------------------------------
@app.route('/user/<username>')
def show_user(username):
return f"👤 用户页面: 你好, {username}!"@app.route('/post/int:post_id')
def show_post(post_id):
# 如果 URL 不是整数,Flask 会自动返回 404,不会进入此函数
return f"📝 文章 ID: {post_id} (类型: {type(post_id).name})"@app.route('/file/path:filename')
def serve_file(filename):
# path 转换器允许包含斜杠
return f"📂 文件路径: {filename}"---------------------------------------------------------
3. HTTP 方法处理 (Methods)
---------------------------------------------------------
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
data = request.form.get('username') or request.json.get('username') if request.is_json else '匿名'
return jsonify({"status": "success", "message": f"用户 {data} 登录成功 (POST)"})
else:
return "🔐 登录页面 (GET): 请提交 POST 请求进行测试。"---------------------------------------------------------
4. URL 规范化测试 (Trailing Slashes)
---------------------------------------------------------
带斜杠:访问 /projects 会自动 308 重定向到 /projects/
@app.route('/projects/')
def projects():
return "🚀 项目列表 (规范 URL 带斜杠)"不带斜杠:访问 /about/ 会直接 404
@app.route('/about')
def about():
return "ℹ️ 关于页面 (规范 URL 不带斜杠)"---------------------------------------------------------
5. 类视图 (MethodView) - RESTful 风格
---------------------------------------------------------
class ItemAPI(MethodView):
def get(self, item_id=None):
if item_id is None:
return jsonify({"action": "list", "data": ["item1", "item2"]})
return jsonify({"action": "get", "id": item_id, "data": f"Details of {item_id}"})def post(self): return jsonify({"action": "create", "status": "created"}), 201 def put(self, item_id): return jsonify({"action": "update", "id": item_id, "status": "updated"}) def delete(self, item_id): return jsonify({"action": "delete", "id": item_id, "status": "deleted"}), 204注册类视图路由
app.add_url_rule('/items/', view_func=ItemAPI.as_view('items_list'))
app.add_url_rule('/items/int:item_id', view_func=ItemAPI.as_view('items_detail'))---------------------------------------------------------
6. 错误处理 (404 & 405)
---------------------------------------------------------
@app.errorhandler(404)
def not_found(e):
return jsonify({"error": "404 Not Found", "message": "哎呀,路由没找到!"}), 404@app.errorhandler(405)
def method_not_allowed(e):
return jsonify({"error": "405 Method Not Allowed", "message": "该 URL 不支持此 HTTP 方法。"}), 405---------------------------------------------------------
7. 启动时打印路由表 (调试用)
---------------------------------------------------------
def print_routes():
print("\n" + "="*50)
print("🗺️ 已注册的路由表:")
print("="*50)
with app.app_context():
for rule in app.url_map.iter_rules():
methods = ','.join(sorted(rule.methods - {'HEAD', 'OPTIONS'}))
print(f"{methods:10} | {rule.rule:<30} -> {rule.endpoint}")
print("="*50 + "\n")if name == 'main':
print_routes()
# debug=True 开启热重载和详细错误页
app.run(debug=True, port=5000)
2. 如何运行
-
安装 Flask (如果尚未安装):
pip install flask -
运行脚本:
python test_routes.py启动后,你会看到终端打印出所有注册的路由表。
3. 测试指南 (如何验证)
你可以使用 浏览器 访问简单链接,或使用 curl (命令行工具) 测试复杂的 HTTP 方法和错误情况。
A. 基础与动态路由测试
| 测试内容 | 浏览器/Curl 命令 | 预期结果 |
|---|---|---|
| 首页 | http://127.0.0.1:5000/ |
显示欢迎信息 |
| 字符串参数 | http://127.0.0.1:5000/user/alice |
显示 "你好, alice" |
| 整数参数 | http://127.0.0.1:5000/post/123 |
显示 "文章 ID: 123", 类型为 int |
| 整数参数失败 | http://127.0.0.1:5000/post/abc |
404 Not Found (因为 'abc' 不是 int) |
| 路径参数 | http://127.0.0.1:5000/file/images/logo.png |
显示完整路径,包含斜杠 |
B. HTTP 方法测试 (使用 Curl)
| 测试内容 | Curl 命令 | 预期结果 |
|---|---|---|
| GET Login | curl http://127.0.0.1:5000/login |
显示 "登录页面 (GET)..." |
| POST Login | curl -X POST -H "Content-Type: application/json" -d '{"username":"bob"}' http://127.0.0.1:5000/login |
返回 JSON: {"status": "success"...} |
| Method Not Allowed | curl -X DELETE http://127.0.0.1:5000/login |
405 Error (JSON 格式错误提示) |
C. URL 规范化 (斜杠) 测试
| 测试内容 | 浏览器/Curl 命令 | 预期结果 |
|---|---|---|
| 目录重定向 | curl -I http://127.0.0.1:5000/projects |
308 Permanent Redirect 到 /projects/ |
| 目录正常访问 | curl http://127.0.0.1:5000/projects/ |
200 OK, 显示内容 |
| 文件 404 | curl http://127.0.0.1:5000/about/ |
404 Not Found (注意末尾的斜杠) |
D. MethodView (RESTful API) 测试
| 测试内容 | Curl 命令 | 预期结果 |
|---|---|---|
| 获取列表 | curl http://127.0.0.1:5000/items/ |
JSON: {"action": "list"...} |
| 获取详情 | curl http://127.0.0.1:5000/items/5 |
JSON: {"action": "get", "id": 5...} |
| 创建资源 | curl -X POST http://127.0.0.1:5000/items/ |
201 Created , JSON: {"action": "create"...} |
| 更新资源 | curl -X PUT http://127.0.0.1:5000/items/5 |
JSON: {"action": "update"...} |
| 删除资源 | curl -X DELETE http://127.0.0.1:5000/items/5 |
204 No Content (通常无返回体) |
4. 观察重点
- 类型安全 :尝试访问
/post/abc,你会发现它直接报 404,而不是进入函数报错。这是 Flask 路由转换器的功劳。 - 重定向行为 :注意访问
/projects(无斜杠) 时,Curl 的-I参数会显示Location: /projects/和状态码308。 - 自动 HEAD/OPTIONS :虽然代码里没写
HEAD,但如果你运行curl -I http://127.0.0.1:5000/,它会成功返回头部信息。 - JSON 响应 :现代 Flask 应用通常返回 JSON,注意看
login和items接口的返回格式。