【问题标题】:Laravel - regex route match everything but not exactly one or more wordLaravel - 正则表达式路由匹配所有内容,但不完全匹配一个或多个单词
【发布时间】:2020-04-20 07:50:46
【问题描述】:

我做了一个类似的路线

Route::get('/{url1}', function ($url1) {
    return ' url1: '.$url1;
})
->where('url1', '^(?!(string1|string2)$)');

并访问如下网址:
- domain/abc 未找到 => 不正确 ??
- domain/string1 未找到 => 正确

还有更多,当我这样做时

Route::get('/{url1}/{url2}', function ($url1) {
    return ' url1: '.$url1;
})
->where('url1', '^(?!(string1|string2)$)');

并访问网址,例如:
- domain/abc/abc 未找到 => 不正确 ???
- domain/string1/abc 未找到 => 正确

如何解决这个问题,谢谢

【问题讨论】:

  • 字符串是静态的还是动态的?
  • @MiteshRathod string1 和 string2 是特殊词(它是静态的)
  • 只有这 2 个字符串在 url 中不接受,对吗?
  • @MiteshRathod 是的,url 中不接受 string1 或 string2
  • 请添加您的正则表达式。

标签: regex laravel laravel-5 laravel-routing


【解决方案1】:

请试试这个 对于第一种情况:

Route::get('/{url1}', function ($url1) {
    return ' url1: '.$url1;
})->where('url1', '^!(string1|string2)$');

对于第二种情况:

Route::get('/{url1}/{url2}', function ($url1, $url2) {
    return ' url1: '.$url1 . ' url2: '.$url2;
})->where('url1', '^!(string1|string2)$');

希望对你有帮助:)

【讨论】:

  • thankiu 但是,我只是尝试使用domain/abc 进行第一次尝试,但没有找到?
【解决方案2】:

试试这个

 Route::get('{url1}', function ($url1) {
    return ' url1: '.$url1;
})->where('url1','^(?!string1$|string2$)([a-zA-Z0-9-]+)');

实现这一目标的不干净方法

Route::get('{url1}/{url2?}', function ($url1,$url2 = null) {
        if ($url2 == "string1" || $url2 == "string2" || $url1 == "string1" || $url1 == "string2") {
            return "false";
        } else {
            return "true";
        }
});

我尝试过的另一种方法是使用RouteServiceProvider.php

像这样更改您的boot()

public function boot()
    {
        //
        Route::pattern('url1', '^(?!string1$|string2$)([a-zA-Z0-9-]+)');
        Route::pattern('url2', '^(?!string1$|string2$)([a-zA-Z0-9-]+)');
        parent::boot();
    }

【讨论】:

  • 谢谢,但效果不佳。我只是尝试domain/string1 => 它仍然找到
  • @DeLe 奇怪我尝试过相同的代码,但它s excluding 127.0.0.1:8000/domain/string2, 127.0.0.1:8000/domain/string1` 并为http://127.0.0.1:8000/domain/abc工作
  • 对不起domain, domain 喜欢http://127.0.0.1:8000,你应该试试127.0.0.1:8000/string1, ...
  • @DeLe 已更新,立即查看。
  • @DeLe 使用RouteServiceProvider查看更新的答案。
【解决方案3】:

经过测试,我认为不可能完全实现您想要的。似乎当您想要排除 string1string2 时,您需要同意以 string1string2 开头的字符串也将被排除(例如 string1aaa)。

使用此类路线时:

Route::get('/{url1}', function ($url1) {
    return ' url1: '.$url1;
})->where('url1', '(?!string1|string2)[^\/]+');


Route::get('/{url1}/{url2}', function ($url1, $url2) {
    return ' url1: '.$url1. ' # '.$url2;
})->where('url1', '(?!string1|string2)[^\/]+');

结果将是:

domain/abc - found, correct
domain/string1 - not found, correct
domain/abc/abc - found, correct
domain/string1/abc - not found, correct
domain/string1aaa - not found, I believe you need to accept this
domain/string1aaa/abc - not found, I believe you need to accept this

我相信这种限制来自 Laravel 而不是来自正则表达式。如果您还需要接受以string1string2 开头的参数,我相信您需要像这样以手动方式进行:

Route::get('/{url1}', function ($url1) {
    if (!preg_match('#^(?!string1$|string2$)[^\/]*$#', $url1)) {
        abort(404);
    }

    return ' url1: '.$url1;
});


Route::get('/{url1}/{url2}', function ($url1, $url2) {
    if (!preg_match('#^(?!string1$|string2$)[^\/]*$#', $url1)) {
        abort(404);
    }

    return ' url1: '.$url1. ' # '.$url2;
});

【讨论】:

    【解决方案4】:

    试试这个正则表达式:

        Route::get('/{url1}', function ($url1) {
            return 'url: '.url1;
        })->where('url1', '^(?!(string1|string2)$)(\S+)');
    

    【讨论】:

    • thankiu,但是当我尝试http://domain/string1/abc 时它仍然找到?
    【解决方案5】:

    我发现写两条路线更清晰,而不是写一条路线来匹配任何除了某些静态字符串:一条路线匹配某些静态字符串,另一条路线匹配其他所有内容。

    // route that matches forbidden static strings, optionally with a postfix slug
    $router->get('/{forbidden}/{optional_path?}', function () {
        return response('Not found', 404);
    })->where([ 'forbidden' => '(?:string1|string2)', 'optional_path' => '.*' ]);
    
    // route that matches anything else (order of definition matters, must be last)
    // might also consider using Route::fallback(), but I prefer to leave that
    // alone in case my future self changes this below and opens up a hole
    $router->get('/{anything?}', function () {
        return response('Found', 200);
    })->where([ 'anything' => '.*' ]);
    

    这会导致*

    • domain => 找到 200 个
    • domain/ => 找到 200 个
    • domain/abc => 找到 200 个
    • domain/string1 => 404 未找到
    • domain/string1/ => 404 未找到
    • domain/string1/abc => 404 未找到
    • domain/string10 => 找到 200 个
    • domain/string10/ => 找到 200 个
    • domain/string10/abc => 找到 200 个
    • domain/string2 => 404 未找到
    • domain/string2/ => 404 未找到
    • domain/string2/abc => 404 未找到
    • domain/string20 => 找到 200 个
    • domain/string20/ => 找到 200 个
    • domain/string20/abc => 找到 200 个

    我觉得这更清楚,因为我不必考虑排除项。相反,我可以考虑完全匹配我想要禁止的内容,然后让 Laravel 对其他所有内容做出反应(失败开放策略)。这可能不符合您的设计标准,但我相信它会产生更清晰的代码。

    此外,代码的性能更高。 ?! 必须回溯,这在定义上比前向匹配更昂贵。

    我手头没有 Laravel 环境,但我会冒险猜测您的尝试为什么没有奏效。 Laravel 使用 does not support lookarounds on slugs 的 Symfony 路由器。 IIRC,当检测到环视时,Symfony 将环视应用到整个 URL,而不是您将模式绑定到的 slug。这与开发人员对锚 (^, $) 和贪婪 (*) 元字符如何工作的想法相混淆。这可能会导致在尝试使其工作时体验不佳,因为开发人员在一个假设下运行,但底层库在另一个假设下运行。


    * 完全公开,我是为 Lumen 写的,然后在心里把它转换成 Laravel 格式。可能存在一些翻译错误。这是原始流明:

    $router->get('/{forbidden:(?:string1|string2)}[/{optional_path:.*}]', function () {
        return response('Not found', 404);
    });
    $router->get('{anything:.*}', function () {                                   
        return response('Found', 200);
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多