我最初有一个这样定义的路由:
get ':foo(/:bar)/:baz(/:qux)/:slug', to: 'application#show', constraints: { baz: /.+?-d+/ }, as: test
在这个配置下,如果我在路径中省略:bar
,它会工作得很好:foo/baz-123/qux-quux/test
控制器返回如下参数:{foo: 'foo', baz: 'baz-123', qux: 'qux-quux', slug: 'test'}
现在,规范改变了,我需要在baz
约束中接受字母数字字符,所以我这样做:
get ':foo(/:bar)/:baz(/:qux)/:slug', to: 'application#show', constraints: { baz: /.+?-[a-z0-9]+/ }, as: test
使用此配置,参数映射开始表现不同。相同的路径foo/baz-123/qux-quux/test
在控制器中返回以下参数:
{foo: 'foo', bar: 'baz-123', baz: 'qux-quux', slug: 'test'}
我认为它正在发生,因为qux-quux
也匹配约束,所以我将其更改为/.+?-(?=.*d+)[a-z0-9]+/
,以确保:baz
参数在-
之后的第二部分至少需要一个数字。
但是,即使qux-quxx
不匹配约束,控制器仍然返回相同的错误映射。
在多个可选段的情况下,Rails如何映射参数?
您的路由路径:foo(/:bar)/:baz(/:qux)/:slug
包含5个段,3个强制段和2个可选段,因此请求路径将在以下情况下进行验证:
- 它包含足够的5段
- 它包含三个片段,在这种情况下,显然这是三个强制部分
- 它包含4段,这里有2个有效的情况:
:foo/:bar/:baz/:slug
,:foo/:baz/:qux/:slug
。
当你为一个段设置约束时,匹配器将检查该段是否与约束匹配。正如你所看到的,在前2种情况下,匹配器知道段:baz
在哪里,但是在4个段的情况下,匹配器必须从2个有效的情况中选择一个,所以它会混淆,因此它将逐个检查段,并选择最后一个匹配的。
/.+?-[a-z0-9]+/ =~ "baz-123" # matched
/.+?-[a-z0-9]+/ =~ "qux-quux" # matched <-- the last one
{foo: 'foo', bar: 'baz-123', baz: 'qux-quux', slug: 'test'}
我猜约束段的优先级高于其他段,因此首先检查它们,之后根据约束段的位置检查其他段,在您的情况下,在验证段:baz
后,其左侧的段将是:bar
,:slug
段在右侧(可选段:qux
将被忽略)。
当将约束更改为/.+?-(?=.*d+)[a-z0-9]+/
/.+?-(?=.*d+)[a-z0-9]+/ =~ "baz-123" # matched <-- baz
/.+?-(?=.*d+)[a-z0-9]+/ =~ "qux-quux" # not matched
# so the params should be (the `:bar` will be ignored)
{foo: 'foo', baz: 'baz-123', qux: 'qux-quux', slug: 'test'}
在我看来,如果:bar
和:baz
之间有任何不同的模式,最好也为:bar
段设置约束,这样匹配器将逐个检查,不会再混淆了。
get ':foo(/:bar)/:baz(/:qux)/:slug', to: 'application#show', constraints: {
bar: /.*/,
baz: /.+?-(?=.*d+)[a-z0-9]+/
}, as: test
另一种方法,我认为,你可以为段:bar
设置默认值,在你的控制器中你可以处理这个默认值,所以现在没有任何情况下匹配器会混淆。
还有一件事,您的最后一个regexp/.+?-(?=.*d+)[a-z0-9]+/
是greedy
,这将吞噬所有路径而不尊重飞溅/
,因此路径foo/bar/baz-x/test
将失败,但路径foo/bar/baz-x/x1x
将通过,因为字符-
之后有一个数字。您可以将贪心部分(?=.*d+)
替换为(?=[^/]*d+)
来修复它。所以要小心使用regexp约束。