bigywater.tech
Flask · 19

自定义 URL Converter

想让 URL 里的 /u/ 直接给视图一个用户对象,怎么写转换器?转换失败会怎样?url_for 又怎么反过来?这篇写两个转换器并实测。

2
一个转换器必须提供的两个方法:to_python 与 to_url(本篇实现)
404
转换失败(ValidationError)与 regex 不匹配的最终状态码
-0.3 µs
自定义转换器相对内置 int 的每请求差(test client)

两个转换器

实测 · 本机执行输出
  转换器定义与注册:
      1 | class ListConverter(BaseConverter):
      2 |     regex = r"[\w-]+(?:,[\w-]+)*"
      3 |     def to_python(self, value): return value.split(",")
      4 |     def to_url(self, values): return ",".join(BaseConverter.to_url(self, v) for v in values)
      5 | class UserConverter(BaseConverter):
      6 |     regex = r"\d+"
      7 |     def to_python(self, value):
      8 |         uid = int(value)
      9 |         if uid not in USERS: raise ValidationError()
     10 |         return {"id": uid, "name": USERS[uid]}
     11 |     def to_url(self, user): return str(user["id"])
     12 | app.url_map.converters["list"] = ListConverter
     13 | app.url_map.converters["user"] = UserConverter
  GET /tags/a,b,c    → 200 list:['a', 'b', 'c']
  GET /tags/solo     → 200 list:['solo']
  GET /tags/a,,b     → 404 
  GET /u/1           → 200 dict:{'id': 1, 'name': 'ann'}
  GET /u/2           → 200 dict:{'id': 2, 'name': 'bob'}
  GET /u/3           → 404 
  GET /u/abc         → 404 

上一篇:path、int、float、uuid 转换器;下一篇:路由中的尾部斜杠行为。

to_url

实测 · 本机执行输出
  url_for("tags", tags=["x","y","z"]) → /tags/x,y,z
  url_for("user", u={"id": 2, ...}) → /u/2
  url_for("tags", tags=["a b","c"]) → /tags/a%20b,c (to_url 里调用 BaseConverter.to_url 做了 URL 转义)

注册时机

实测 · 本机执行输出
  使用未注册的 list 转换器 → LookupError: the converter 'list' does not exist

ValidationError 与 abort

实测 · 本机执行输出
  GET /u/3   → 404;触发的回调:['before_request']
  GET /v/1   → 404;触发的回调:['before_request', 'view']

开销

实测 · 本机执行输出
  自定义 user 转换器 73.5 µs/请求;内置 int 73.8 µs/请求;差 -0.3 µs

分步演示

自定义转换器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:转换器 weight 属性对优先级的影响(只在上一篇读到内置转换器的权重)、转换器里访问数据库的事务边界问题、part_isolating 属性的作用、给转换器传参数(如 )的写法
    京ICP备2026067502号-1京公网安备11010502063487号