bigywater.tech
Flask · 30

URL 中的 Unicode 与编码

浏览器发来的中文 URL,Flask 里到底拿到什么?%2F 是斜杠吗?非法编码会怎样?url_for 生成的链接长什么样?这篇用 test client 和真实的原始套接字都验证一遍。

�
非法 UTF-8 被解码成的替换字符(不报 400)
+ / %20 / %2B
查询字符串里:空格 / 空格 / 加号
404
原始(未编码)UTF-8 请求行在 gunicorn 下的状态码

路径里的中文

实测 · 本机执行输出
  GET /%E7%94%A8%E6%88%B7/%E5%BC%A0%E4%B8%89 → 200 name='张三' path='/用户/张三' PATH_INFO='/ç\x94¨æ\x88·/å¼\xa0ä¸\x89'
  GET /用户/张三                             → 200 name='张三' path='/用户/张三' PATH_INFO='/ç\x94¨æ\x88·/å¼\xa0ä¸\x89'
  GET /%e7%94%a8%e6%88%b7/ann            → 200 name='ann' path='/用户/ann' PATH_INFO='/ç\x94¨æ\x88·/ann'
  (WSGI 的 PATH_INFO 是把 UTF-8 字节按 latin-1 解成的 str,所以 environ 里看到的是乱码形式,Werkzeug 再把它还原)

上一篇:子域名路由;下一篇:Werkzeug Request 对象。

查询字符串

实测 · 本机执行输出
   args={'name': 'a b', 'x': '中文', 'plus': '1+1', 'sp': 'a b'} full_path='/q?name=a+b&x=%E4%B8%AD%E6%96%87&plus=1%2B1&sp=a%20b' query_string=b'name=a+b&x=%E4%B8%AD%E6%96%87&plus=1%2B1&sp=a%20b' url='http://localhost/q?name=a+b&x=中文&plus=1%2B1&sp=a%20b'

编码的斜杠

实测 · 本机执行输出
  GET /seg/a%2Fb       → 404 
  GET /seg/a/b         → 404 
  GET /files/a%2Fb     → 200 p='a/b'
  GET /files/a/b       → 200 p='a/b'

非法编码

实测 · 本机执行输出
  GET /seg/%FF       → 200 "s='�'"
  GET /seg/%E4%B8    → 200 "s='�'"
  GET /seg/%ZZ       → 200 "s='%ZZ'"
  GET /seg/a%00b     → 200 "s='a\\x00b'"

url_for 与 redirect

实测 · 本机执行输出
  url_for("user", name="张三") → /%E7%94%A8%E6%88%B7/%E5%BC%A0%E4%B8%89
  url_for("q", name="中文 空格&号", x="a/b") → /q?name=%E4%B8%AD%E6%96%87+%E7%A9%BA%E6%A0%BC%26%E5%8F%B7&x=a/b
  redirect("/目标/页 面?x=中文") → Location = /%E7%9B%AE%E6%A0%87/%E9%A1%B5%20%E9%9D%A2?x=%E4%B8%AD%E6%96%87

原始套接字

实测 · 本机执行输出
  已编码                    → HTTP/1.1 200 OK | 'name=张三 len=2'
  原始 UTF-8 字节            → HTTP/1.1 404 NOT FOUND | '<!doctype html>\n<html lang=en>\n<title>404 Not Foun'
  原始 latin-1 非法字节        → HTTP/1.1 404 NOT FOUND | '<!doctype html>\n<html lang=en>\n<title>404 Not Foun'

分步演示

URL 编码
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动
    • 没有验证:浏览器对地址栏里中文的实际编码(用的是 UTF-8)、IDN 域名(Punycode)、uWSGI 与其它服务器对未编码请求行的处理、URL 长度上限、gunicorn 返回 404 的内部原因
    京ICP备2026067502号-1京公网安备11010502063487号