Laravel - 正则表达式路由匹配所有内容,但不完全匹配一个或多个单词

Posted

技术标签:

【中文标题】Laravel - 正则表达式路由匹配所有内容,但不完全匹配一个或多个单词【英文标题】:Laravel - regex route match everything but not exactly one or more word 【发布时间】: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 请添加您的正则表达式。 【参考方案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);
);

【讨论】:

以上是关于Laravel - 正则表达式路由匹配所有内容,但不完全匹配一个或多个单词的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式,匹配除 \r \n 之外的所有内容作为普通字符

匹配正则表达式中的相似字符串,但不是所有出现 [关闭]

普罗米修斯与正则表达式查询不匹配

php 正则表达式 匹配网站内容

必须包含字符串中所有字符的正则表达式匹配

Laravel 4.2 正则表达式路由约束,失败