【问题标题】:Rails route optional segments mapping issueRails 路由可选段映射问题
【发布时间】:2021-10-16 09:06:31
【问题描述】:

我最初有这样定义的路线:

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 如何在多个可选段的情况下映射参数?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-5 rails-routing


    【解决方案1】:

    您的路由路径: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 个有效案例中的一个,所以它会混淆,因此它将逐个检查段,然后选择最后一个匹配的。

    /.+?\-[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+) 来修复它。所以要小心正则表达式的约束。

    【讨论】:

    • 感谢您的详细解答。现在很清楚了。
    猜你喜欢
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 2011-11-11
    • 1970-01-01
    相关资源
    最近更新 更多