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】:
经过测试,我认为不可能完全实现您想要的。似乎当您想要排除 string1
和 string2
时,您需要同意以 string1
和 string2
开头的字符串也将被排除(例如 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 而不是来自正则表达式。如果您还需要接受以string1
和string2
开头的参数,我相信您需要像这样以手动方式进行:
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 - 正则表达式路由匹配所有内容,但不完全匹配一个或多个单词的主要内容,如果未能解决你的问题,请参考以下文章