The Art of Routing: Variable Rules, HTTP Methods and Unique URLs

📂 Stage: Stage 1 - Breaking the ice and setting sail (Basics) 🔗 Related chapters: 初识 Flask · Jinja2 模板引擎


Routing, in a nutshell, is a mechanism that binds the URL path visited by the browser to the Python processing function we wrote. Flask's routing system is flexible and simple, which is one of the core reasons why it is quick to use and popular. Today we will go through this basic but crucial module together.


1. Start with the simplest route

1.1 Basic decorator writing method

Flask uses@app.route()Decorator to define routes, the parameter is the URL path you want to bind. Let’s write the most intuitive code first:

from flask import Flask

# 初始化 Flask 应用
app = Flask(__name__)

# 绑定根路径 /
@app.route("/")
def index():
    return "<h1>欢迎来到道满的 Flask 技术站!</h1>"

# 绑定 /about 路径
@app.route("/about")
def about():
    return "<p>这里是道满,专注于简洁好用的 Web 技术</p>"

if __name__ == "__main__":
    # debug=True 开启调试模式,修改代码自动重启
    app.run(debug=True, port=5001)

After starting, access in the browserhttp://127.0.0.1:5001andhttp://127.0.0.1:5001/about, you can see the corresponding page.

1.2 The "icing" principle of decorators (simple version)

Many novices may find decorators mysterious, but in Flask routing, they are just syntax sugar—essentially registering functions into the application’s routing map.

# 下面的装饰器写法
@app.route("/")
def index():
    return "<h1>欢迎</h1>"

# 其实等价于这段原生 Python:
def unwrapped_index():
    return "<h1>欢迎</h1>"

index = app.route("/")(unwrapped_index)

It is enough to understand this principle. In daily development, you should still use decorators directly. The code is concise and easy to read.


2. Dynamic routing: let the URL have "variables"

static/aboutOnly fixed content can be displayed. If you want to display articles from different users’ personal homepages and IDs, it’s impossible to write thousands of them.@app.route("/user/john")Bar? At this time, you need path variables, which is dynamic routing.

2.1 The three most commonly used ways to write variables

from flask import Flask

app = Flask(__name__)

# 1. 默认是字符串类型(不含斜杠)
@app.route("/user/<username>")
def show_user(username):
    # username 会自动作为参数传入
    return f"<h1>用户资料页:{username}</h1>"

# 2. int 类型(自动转换成整数,匹配不到时返回 404)
@app.route("/post/<int:post_id>")
def show_post(post_id):
    return f"<h1>博客文章 ID:{post_id}</h1>"

# 3. path 类型(可以包含斜杠,适合文件路径、多级分类)
@app.route("/file/<path:file_path>")
def show_file(file_path):
    return f"正在访问文件:{file_path}"

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

2.2 Complete variable type cheat sheet

ConverterDescriptionMatch Example URL
stringDefault, any string without slashes/user/john_doe
intPositive integer, automatically converted toint/post/123
floatPositive floating point number, automatically converted tofloat/product/rate/4.9
pathAny string containing slashes (similar tostringbut allowed//assets/css/style.css
uuidStrictly matches the UUID format and automatically converts touuid.UUID/api/item/550e8400-e29b-...
anyLimit several fixed candidate values ​​(select one from multiple)/order/<any(pending,completed)>

inanyConverters are great for handling "fixed options but don't want to write multiple routes" scenarios:

@app.route("/order/<any(pending, completed, canceled):status>")
def show_order_list(status):
    return f"正在查看状态为「{status}」的订单列表"

# ✅ 访问 /order/pending
# ✅ 访问 /order/canceled
# ❌ 访问 /order/shipping → 404 (不在 any 指定的范围内)

2.3 Multiple variable combinations (blog archive example)

Variables can be combined in any way as long as they comply with the URL specification:

from flask import Flask
from datetime import datetime

app = Flask(__name__)

@app.route("/blog/<int:year>/<int:month>/<slug>")
def blog_archive(year, month, slug):
    try:
        target_date = datetime(year, month, 1)
    except ValueError:
        return "日期格式不正确!", 400
    return f"""
        <h1>{target_date.strftime('%Y年%m月')} 的文章</h1>
        <p>文章标题链接:{slug}</p>
    """

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

3. HTTP method: Control the "action" of the request

The browser sends it by default by pressing Enter in the address bar.GETrequest, but there is much more to web development than just that. Also commonly used arePOSTPUTDELETEetc., they essentially tell the server "what do you want to do with this resource".

3.1 Multi-method binding (login form example)

The login function usually requires two actions: usingGETTo display the form, usePOSTSubmit the form. we can passmethodsParameters allow the same route to handle both requests.

from flask import Flask, render_template, request, redirect, url_for, flash

app = Flask(__name__)
# flash 消息需要设置 secret_key
app.secret_key = "your-secret-key-keep-it-safe"

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        # 这里省略数据库验证,直接模拟登录成功
        flash("登录成功!")
        # 重定向到首页(利用下一节学的 url_for)
        return redirect(url_for("index"))
    # GET 请求则展示登录表单
    return render_template("login.html")

@app.route("/")
def index():
    return render_template("index.html")

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

💡 Tips:request.formSpecifically used to obtain form data submitted by POST; if the upload is JSON, you can userequest.get_json()

3.2 RESTful style routing design

REST is currently the most popular API design principle. The core concept is: URL describes resources, and HTTP methods describe operations. Take the "article" resource as an example:

ActionsTraditional URLs (not recommended)RESTful URLs (recommended)HTTP methods
View article list/get_articles/articlesGET
View single article/show_article?id=123/articles/123GET
Show create article form/create_article_form/articles/newGET
Submit a request to create an article/submit_article/articlesPOST
Show edit article form/edit_article_form?id=123/articles/123/editGET
Update article/update_article/articles/123PUT/PATCH
Delete article/delete_article?id=123/articles/123DELETE

⚠️ Note: The browser only supports nativeGETandPOST, if you want to send it in an HTML formPUTPATCHDELETERequest, you need to use hidden fields<input type="hidden" name="_method" value="PUT">And cooperate with Flask'smethodoverrideMiddleware (will be expanded on in subsequent chapters).


4. URL reverse generation: Say goodbye to the trouble of hard coding

Hardcoding URLs is a big no-no in development. For example, if you/aboutchanged to/about-us, all places that reference this address (HTML links, redirect codes) must be modified one by one, and it is easy to miss. Flask providesurl_forfunction, generate the corresponding URL through the name of the view function, which perfectly solves this problem.

4.1 url_forBasic usage of

from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route("/")
def index():
    # 在 Python 代码里生成 URL
    test_url1 = url_for("show_user", username="alice")      # /user/alice
    test_url2 = url_for("blog_archive", year=2026, month=3, slug="flask-routing")  # /blog/2026/3/flask-routing
    return f"""
        <h1>道满的 Flask 技术站</h1>
        <a href='{url_for("about")}'>关于我</a><br>
        <a href='{test_url1}'>Alice 的个人页</a><br>
        <a href='{test_url2}'>Flask 路由教程</a><br>
    """

@app.route("/about")
def about():
    return "关于道满"

@app.route("/user/<username>")
def show_user(username):
    return f"用户资料页:{username}"

@app.route("/blog/<int:year>/<int:month>/<slug>")
def blog_archive(year, month, slug):
    return f"{year}{month}月的文章:{slug}"

# 跳转到 Alice 的个人页
@app.route("/goto-alice")
def goto_alice():
    return redirect(url_for("show_user", username="alice"))

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

4.2 Use with Jinja2 template

In actual development, links are mostly written in Jinja2 templates, and their usage is almost the same as Python code:

<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>道满的 Flask 技术站</title>
</head>
<body>
    <h1>道满的 Flask 技术站</h1>
    <!-- 无参数路由 -->
    <a href="{{ url_for('about') }}">关于我</a><br>
    <!-- 带变量的路由 -->
    <a href="{{ url_for('show_user', username='bob') }}">Bob 的个人页</a><br>
    <!-- 额外的查询参数会自动拼接在 ? 后面 -->
    <a href="{{ url_for('blog_archive', year=2026, month=3, slug='flask-routing', sort='latest') }}">
        最新的 Flask 路由教程
    </a>
</body>
</html>

💡 Best Practice: Use it unconditionally whenever you need to generate URLs inside your applicationurl_for, which will make refactoring incredibly easy.


5. Unique URLs: Flask’s “smart” trailing slash handling

Have you noticed that visitinghttps://flask.palletsprojects.com/andhttps://flask.palletsprojects.comIt's the same page, but visit/about/and/aboutMaybe the behavior is different? Flask handles trailing slashes carefully to avoid the SEO embarrassment of "two URLs for the same resource".

5.1 Flask’s default behavior

from flask import Flask

app = Flask(__name__)

# 路由没有尾部斜杠 → 行为类似文件
@app.route("/about")
def about():
    return "关于页面(没有尾部斜杠)"

# 路由有尾部斜杠 → 行为类似文件夹
@app.route("/blog/")
def blog_list():
    return "博客列表(有尾部斜杠)"

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

Test it out:

  1. Visit/about→ ✅Return normally
  2. Visit/about/→ 🔀 Flask automatically 301 redirects to/about
  3. Access/blog/→ ✅Return normally
  4. Access/blog→ 🔀 Flask automatically 301 redirects to/blog/

Flask's approach is: if the route defined does not have a slash, access to the address with a slash will be redirected to the version without a slash; and vice versa. This always ensures that there is only one authoritative address for a resource.

5.2 Should I add a trailing slash?

It is recommended to follow the following intuition:

  • Static pages (such as/about/contact)→ without slash, like file
  • List/collection pages (e.g./blog//users/) → Add slash, like a folder
  • API interface (e.g./api/articles/api/users/123)→ Uniform without slash, more concise

6. Summary

Today we systematically learned the core capabilities of Flask routing:

  1. Basic Decorator:@app.route("/path")Bind URL to view function
  2. Dynamic Routing:<converter:variable>Let URLs carry mutable data
  3. HTTP Method: PassmethodsParameters handle different request actions, moving towards RESTful design
  4. Reverse generation:url_for("view_name", arg=val)Eliminate hard coding
  5. Unique URL: Understand Flask’s trailing slash redirection mechanism

💡 Core design principle: URLs should be predictable, descriptive of the resource, and unique.


🔗 Extended reading