重定向规则

Odoo 网站重定向为什么不是“填个 from/to”就完事:301/302/308/404 与路由失效治理讲透

很多人把 Odoo 重定向理解成一个 URL 映射表,但 website.rewrite 实际同时处理永久迁移、临时跳转、别名式重写、404 屏蔽与路由缓存失效,是一套网站入口治理机制。

网站
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

在 Odoo 里,网站重定向不是一个“Excel 里两列地址一映射”的小功能。

/home/ubuntu/odoo-temp/addons/website/models/website_rewrite.pytests/test_redirect.py 看,website.rewrite 实际承担的是:

  • 页面迁移;
  • 临时引流;
  • 路由别名;
  • 特定入口下线;
  • 以及这些变化对 routing map 的刷新。

所以它真正解决的问题是:

当网站入口发生变化时,如何既不把旧流量摔死,又不让路由系统进入混乱状态。

如果你把它只理解成“跳转表”,后面会很容易踩到 SEO、缓存和路由一致性的坑。


一、为什么 Odoo 要把“重定向”和“重写”分成 301/302/308/404 四种动作

redirect_type 不是简单一个 yes/no,而是四选一:

  • 301 Moved permanently
  • 302 Moved temporarily
  • 308 Redirect / Rewrite
  • 404 Not Found

这四种在 Odoo 里的语义并不一样。

1. 301:旧地址正式退役

适合:

  • 页面永久迁移;
  • 改版后旧 URL 回收;
  • 希望浏览器和搜索引擎长期记住新地址。

2. 302:临时导流

适合:

  • 短期活动页;
  • AB 测试阶段;
  • 临时把某个入口导去另一页,但不想让缓存长期固化。

3. 308:更像“路由别名”而不是普通跳转

源码 help 直接说明了这个用途:

  • 比如把 /shop 改成 /garden
  • 两个 URL 都能存在,但旧入口会自动重定向到新入口语义。

更关键的是,308 会参与 routing map 层面的治理,而不是仅仅作为一个请求后置 fallback。

4. 404:显式下线某个入口

这个非常容易被忽略。

它不是“找不到页面时自然 404”,而是:

  • 即便系统本来有这个 controller / 路由,当前网站也可以有意把它屏蔽掉。

最典型的例子就是:

  • 你装了电商模块,
  • 但某个站点根本不想开放 /shop

这时 404 rewrite 就比你到处硬删模板稳得多。


二、为什么 308 和 404 会触发 routing 缓存失效,而 301/302 不一定

create()write()unlink() 里有一个非常关键的判断:

  • 只要涉及 308404,就会 _invalidate_routing(),最终 clear_cache('routing')

源码注释讲得很清楚:

  • 404 会改变 routing map,可把某入口从映射里拿掉;
  • 308 会给 routing map 增加 alias;
  • 301/302 更多是路由找不到后再走的跳转处理,不一定改 routing map 本身。

这说明 Odoo 对几种 rewrite 的定位不一样:

有些是“请求进来后告诉你去哪儿”,有些是“入口地图本身已经变了”。

这也是为什么 308/404 更重,更需要刷新所有 worker 的路由缓存。


三、为什么 url_to 校验会这么严格

_check_url_to() 里有一整组看起来很“挑剔”的校验,实际上都很有必要。

1. 不能空

301/302/308 都必须同时有 url_fromurl_to

2. 不能以 # 开头

因为 fragment 不是可路由入口,拿它当目标没有意义。

3. base URL 不能和来源一样

否则你等于造了一条自循环规则。

4. 308 的目标必须以 / 开头

因为它是站内路由别名,不是随手跳到任意片段。

5. 308 的动态参数要一一对应

如果 url_from 里有 /<id> 这样的参数,url_to 必须也带上;反之亦然。

这非常关键,因为一旦参数丢了,重写就不再是“别名”,而是在改请求语义。

6. 308 不能把目标设成首页 /

首页切换应该走网站首页机制,不该混进 rewrite 规则里。

7. 308 不能指向已存在页面

如果新目标本来就是一个现成路由,再去把旧入口 alias 到它,路由优先级和匹配行为会变得很容易含混。

这也是 tests/test_redirect.py 里专门断言的点。


四、为什么 website.route 要单独维护一张“全路由目录”

website.route 这个模型很多人平时根本不会注意。

它会通过 ir.http._generate_routing_rules() 把当前系统里所有 GET 路由刷新成一张目录表。

这样做有两个好处:

  1. rewrite 配置时可以有更明确的来源候选;
  2. name_search() 搜不到时,系统还能先 refresh 再查,尽量保证配置界面拿到最新路由。

这说明 Odoo 的 rewrite 不是完全脱离路由系统独立存在的,它会主动和当前路由图同步。

换句话说:

你不是在配一个随意的映射表,而是在给现有网站入口图做手术。


五、为什么 sitemap 行为也会受 rewrite 影响

tests/test_redirect.py 里有个非常值得注意的断言:

  • 创建 308 规则后,_enumerate_pages() 的 sitemap 结果里仍包含旧地址,
  • 但不包含 rewrite 目标页本身作为新增条目。

这说明 Odoo 不想让 sitemap 因为 route alias 突然长出一堆重复入口。

从 SEO 角度看,这个选择很合理:

  • rewrite 的目标是入口治理;
  • 不是让搜索引擎把同一内容当成两套独立页面重新抓。

六、最容易误解的,是把 308 当成更高级的 301

这其实不准确。

在 Odoo 语境里:

  • 301/302 更像结果层的跳转规则;
  • 308 更接近路由层的别名治理。

所以如果你只是做:

  • 旧文章链接迁新地址;
  • 临时活动导流;

通常 301/302 就够了。

而当你想做的是:

  • 改一个 controller 入口名;
  • 对现有路由语义做“别名替换”;
  • 按网站维度下线或改写某入口;

308/404 才更像正确工具。


七、实战里什么时候该选哪一种

选 301

  • 页面永久迁移;
  • SEO 资产需要继承;
  • 旧地址以后不再保留语义。

选 302

  • 临时活动导流;
  • 还没决定新地址是否长期生效;
  • 不想让浏览器和搜索引擎过早固化。

选 308

  • 想把某个路由入口改名;
  • 需要参数位保持一致;
  • 需要动 routing map 语义。

选 404

  • 当前网站明确不想暴露某个已安装模块入口;
  • 希望从入口层直接下线,而不是在模板层遮遮掩掩。

八、实施中最值得提前做的,不是配规则,而是盘点入口资产

在真实项目里,我会先把入口分成三类:

  1. 内容资产入口:文章、专题页、落地页;
  2. 产品功能入口:如 /shop/event
  3. 技术路由入口:带参数的 controller 路由。

三类入口对应的 rewrite 策略其实不一样。

如果一开始不分,后面很容易出现:

  • 把 controller alias 当 SEO 重定向做;
  • 把临时活动页做成永久迁移;
  • 把站点下线需求硬塞到模板里。

结语

Odoo 的 website.rewrite 值得重视,不是因为它能做跳转,而是因为它把网站入口变化这件事认真建模了。

它区分:

  • 是长期迁移还是临时导流;
  • 是路由别名还是普通重定向;
  • 是保留入口还是直接屏蔽入口;
  • 变化是否已经影响 routing map 本身。

所以理解它之后,你会更容易看清楚一个官网改版的本质:

不是把页面换个地址,而是在重写“用户和搜索引擎该如何抵达这套网站”。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。