Rails 路由可选段映射问题
Rails route optional segments mapping issue
我原来有这样定义的路由:
get ':foo(/:bar)/:baz(/:qux)/:slug', to: 'application#show', constraints: { baz: /.+?\-\d+/ }, as: test
使用此配置,如果我在我的路径中省略 :bar
它工作正常:
foo/baz-123/qux-quux/test
控制器returns参数如下:{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
returns控制器中的参数如下:
{foo: 'foo', bar: 'baz-123', baz: 'qux-quux', slug: 'test'}
我认为它正在发生,因为 qux-quux
也匹配约束,所以我将其更改为 /.+?\-(?=.*\d+)[a-z0-9]+/
以确保 :baz
参数在第二部分至少需要一位数字在 -
.
之后
然而,即使 qux-quxx
不匹配约束,控制器仍然 returns 相同的错误映射。
在多个可选段的情况下,Rails如何映射参数?
您的路由路径 :foo(/:bar)/:baz(/:qux)/:slug
包含 5 个段、3 个强制段和 2 个可选段,因此请求路径将被验证当且仅当:
- 它包含足够的 5 个片段
- 它包含 3 个部分,在这种情况下,显然是 3 个必填部分
- 它包含 4 个片段,这里有 2 个有效案例:
:foo/:bar/:baz/:slug
、:foo/:baz/:qux/:slug
。
当您为段设置约束时,匹配器将检查该段是否与约束匹配。正如您在前 2 种情况下看到的那样,匹配器知道段 :baz
在哪里,但是在 4 个段的情况下,匹配器必须选择 2 个有效情况之一,所以它会混淆,因此它将检查段 1一个,然后选择最后一个匹配的。
/.+?\-[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
,并且在您的控制器中您可以处理该默认值,所以现在匹配器不会混淆。
还有一件事,你的最后一个正则表达式 /.+?\-(?=.*\d+)[a-z0-9]+/
是 greedy
,这将吞没所有路径而不考虑启动 /
,因此路径 foo/bar/baz-x/test
将失败但是路径 foo/bar/baz-x/x1x
将通过,因为字符 -
后面有一个数字。您可以将贪婪部分 (?=.*\d+)
替换为 (?=[^\/]*\d+)
来修复它。所以要小心正则表达式约束。
我原来有这样定义的路由:
get ':foo(/:bar)/:baz(/:qux)/:slug', to: 'application#show', constraints: { baz: /.+?\-\d+/ }, as: test
使用此配置,如果我在我的路径中省略 :bar
它工作正常:
foo/baz-123/qux-quux/test
控制器returns参数如下:{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
returns控制器中的参数如下:
{foo: 'foo', bar: 'baz-123', baz: 'qux-quux', slug: 'test'}
我认为它正在发生,因为 qux-quux
也匹配约束,所以我将其更改为 /.+?\-(?=.*\d+)[a-z0-9]+/
以确保 :baz
参数在第二部分至少需要一位数字在 -
.
然而,即使 qux-quxx
不匹配约束,控制器仍然 returns 相同的错误映射。
在多个可选段的情况下,Rails如何映射参数?
您的路由路径 :foo(/:bar)/:baz(/:qux)/:slug
包含 5 个段、3 个强制段和 2 个可选段,因此请求路径将被验证当且仅当:
- 它包含足够的 5 个片段
- 它包含 3 个部分,在这种情况下,显然是 3 个必填部分
- 它包含 4 个片段,这里有 2 个有效案例:
:foo/:bar/:baz/:slug
、:foo/:baz/:qux/:slug
。
当您为段设置约束时,匹配器将检查该段是否与约束匹配。正如您在前 2 种情况下看到的那样,匹配器知道段 :baz
在哪里,但是在 4 个段的情况下,匹配器必须选择 2 个有效情况之一,所以它会混淆,因此它将检查段 1一个,然后选择最后一个匹配的。
/.+?\-[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
,并且在您的控制器中您可以处理该默认值,所以现在匹配器不会混淆。
还有一件事,你的最后一个正则表达式 /.+?\-(?=.*\d+)[a-z0-9]+/
是 greedy
,这将吞没所有路径而不考虑启动 /
,因此路径 foo/bar/baz-x/test
将失败但是路径 foo/bar/baz-x/x1x
将通过,因为字符 -
后面有一个数字。您可以将贪婪部分 (?=.*\d+)
替换为 (?=[^\/]*\d+)
来修复它。所以要小心正则表达式约束。