路由与视图函数:路由定义、动态路由、URL构建(url_for)、HTTP方法

各位同学,今天我们来聊聊 Flask 里最核心、最基础的东西——路由与视图函数。说白了,这就是你后端应用的「门牌号系统」。用户访问哪个地址,该由哪段代码来处理,全看路由怎么配。

我个人习惯把路由理解成一张「地图」。你定义好路径,Flask 就知道把请求送到哪个函数手里。这个机制看起来简单,但用好了,能让你的项目结构清晰十倍。

一、路由定义:最基础的玩法

先看一个最简单的例子。你写一个 Flask 应用,想让用户访问根路径 / 时看到一句话:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '欢迎来到我的小站'

if __name__ == '__main__':
    app.run(debug=True)

这里 @app.route('/') 就是路由装饰器。它告诉 Flask:当用户访问 / 时,执行下面的 index() 函数。这个函数我们叫它「视图函数」。

嗯,这里要注意:视图函数必须返回一个响应。可以是字符串、HTML、JSON,或者后面我们会讲到的模板。总之不能返回 None,否则 Flask 会报错。

小技巧: 我刚开始写 Flask 时,经常忘记给视图函数加返回值。后来养成习惯,写完路由立刻写 return,哪怕先写个占位符也行。

二、动态路由:让 URL 活起来

静态路由只能处理固定路径。但实际项目中,URL 往往是动态的。比如用户个人主页:/user/zhangsan/user/lisi。难道要为每个用户写一个路由?当然不。

Flask 提供了动态路由,用 <变量名> 来捕获 URL 中的可变部分:

@app.route('/user/<username>')
def user_profile(username):
    return f'你好,{username}!欢迎来到你的主页。'

访问 /user/小明,视图函数就会收到 username='小明'。是不是很直观?

动态路由还支持类型转换器。比如你想限制参数必须是整数:

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'你正在查看第 {post_id} 篇文章'

Flask 内置了几种转换器:

转换器 说明 示例
string 默认,接受任何不包含斜杠的文本 /user/<string:name>
int 接受正整数 /post/<int:id>
float 接受正浮点数 /price/<float:amount>
path 类似 string,但可以接受斜杠 /path/<path:subpath>
uuid 接受 UUID 字符串 /item/<uuid:item_id>
避坑指南: 我曾经在项目中用 <path:url> 来捕获完整路径,结果忘记处理路径中的特殊字符,导致某些 URL 解析失败。后来我加了一层 URL 编码/解码才搞定。记住:path 转换器虽然方便,但要注意安全过滤。

三、URL 构建:用 url_for 反查路径

写代码时,我们经常需要生成某个视图函数对应的 URL。比如在模板里做链接跳转,或者在重定向时指定目标地址。

硬编码 URL 是最蠢的做法。一旦路由改了,所有硬编码的地方都得跟着改。Flask 提供了 url_for() 函数,可以根据视图函数名动态生成 URL:

from flask import url_for

@app.route('/')
def index():
    # 生成 /user/admin 这个路径
    user_url = url_for('user_profile', username='admin')
    return f'用户主页链接:{user_url}'

@app.route('/user/<username>')
def user_profile(username):
    return f'你好,{username}'

你想想看,如果哪天我把 /user/<username> 改成了 /profile/<username>,只要视图函数名不变,url_for 自动就能生成新路径。省心不省心?

url_for 还支持传递额外参数。比如分页场景:

@app.route('/articles')
def article_list():
    page = request.args.get('page', 1, type=int)
    # 生成 /articles?page=2
    next_page_url = url_for('article_list', page=page+1)
    return f'下一页链接:{next_page_url}'
核心原则: 永远不要硬编码 URL。用 url_for 生成链接,这是 Flask 开发的基本素养。我在团队里定过规矩:谁在代码里写死 URL,谁请全组喝奶茶。

四、HTTP 方法:GET、POST 与更多

路由默认只响应 GET 请求。但实际开发中,我们经常需要处理 POST、PUT、DELETE 等请求。比如提交表单用 POST,更新资源用 PUT。

Flask 允许你指定路由支持的 HTTP 方法:

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        # 处理登录逻辑...
        return f'登录成功,欢迎 {username}'
    # GET 请求返回登录页面
    return '''
        <form method="post">
            用户名:<input type="text" name="username"><br>
            密码:<input type="password" name="password"><br>
            <input type="submit" value="登录">
        </form>
    '''

同一个路由,根据请求方法不同,执行不同的逻辑。这是 RESTful API 设计的基础。

常用的 HTTP 方法:

  • GET:获取资源,幂等,不改变服务器状态
  • POST:创建资源,非幂等,常用于表单提交
  • PUT:更新资源(全量替换),幂等
  • PATCH:部分更新资源
  • DELETE:删除资源
个人经验: 我见过不少新手把所有请求都写成 GET,然后用 URL 参数传数据。这样做既不安全(参数暴露在 URL 中),也不符合 HTTP 语义。记住:修改数据用 POST/PUT,查询数据用 GET。这是后端开发的基本礼仪。

五、综合示例:一个简单的博客路由设计

把上面这些知识点串起来,我们设计一个博客系统的路由:

from flask import Flask, url_for, request, redirect

app = Flask(__name__)

# 文章列表(GET)
@app.route('/articles')
def article_list():
    page = request.args.get('page', 1, type=int)
    return f'文章列表,第 {page} 页'

# 文章详情(GET)
@app.route('/article/<int:article_id>')
def article_detail(article_id):
    return f'查看文章 ID:{article_id}'

# 创建文章(GET 显示表单,POST 提交数据)
@app.route('/article/create', methods=['GET', 'POST'])
def create_article():
    if request.method == 'POST':
        title = request.form.get('title')
        content = request.form.get('content')
        # 保存到数据库...
        # 创建成功后重定向到文章详情页
        return redirect(url_for('article_detail', article_id=123))
    return '''
        <form method="post">
            标题:<input type="text" name="title"><br>
            内容:<textarea name="content"></textarea><br>
            <input type="submit" value="发布">
        </form>
    '''

# 删除文章(POST 请求,因为删除操作有副作用)
@app.route('/article/<int:article_id>/delete', methods=['POST'])
def delete_article(article_id):
    # 执行删除逻辑...
    return redirect(url_for('article_list'))

这个例子涵盖了路由定义、动态路由、URL 构建和 HTTP 方法。你仔细看,每个路由的职责都很清晰,URL 设计也符合 RESTful 风格。

总结一下:
  • 路由是 URL 到视图函数的映射
  • 动态路由用 <变量> 捕获参数,支持类型转换
  • url_for 根据视图函数名反查 URL,避免硬编码
  • methods 参数控制路由支持的 HTTP 方法
这些是 Flask 路由的基石。后面讲蓝图、RESTful API 时,你会发现所有高级玩法都建立在这些基础之上。

好了,这一章就到这里。下一章我们会深入探讨请求与响应对象——说白了,就是怎么从用户那里拿数据,以及怎么把数据还给用户。到时候见。