Laravel - regex route match everything but not exactly one or more word

眉间皱痕 提交于 2020-01-14 07:27:08

问题


I make a route like

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

and access url like:
- domain/abc not found => incorrect ??
- domain/string1 not found => correct

and more, when i do with

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

and access url like:
- domain/abc/abc not found => incorrect ???
- domain/string1/abc not found => correct

How to fix that thank


回答1:


Please try with this for 1st scenario:

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

for 2nd scenario:

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

i hope this help :)




回答2:


Try this

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

Unclean way to achieve this

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

One more way I have tried in by using RouteServiceProvider.php

Change your boot() like this.

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



回答3:


After tests I think it's impossible to achieve exactly what you want. It seems when you want to exclude string1 and string2 you need to agree that also strings starting with string1 and string2 will be excluded (so for example string1aaa).

When using such routes:

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)[^\/]+');

result will be:

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

I believe such limitation comes from Laravel and not from regex. If you need to accept also parameters starting with string1 and string2 I believe you need to do it in manual way like so:

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:


Try this regex:

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



回答5:


Rather than writing a route to match anything other than certain static strings, I find it clearer to write two routes: one route to match certain static strings, and another route to match everything else.

// 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' => '.*' ]);

Which results in*:

  • domain => 200 Found
  • domain/ => 200 Found
  • domain/abc => 200 Found
  • domain/string1 => 404 Not found
  • domain/string1/ => 404 Not found
  • domain/string1/abc => 404 Not found
  • domain/string10 => 200 Found
  • domain/string10/ => 200 Found
  • domain/string10/abc => 200 Found
  • domain/string2 => 404 Not found
  • domain/string2/ => 404 Not found
  • domain/string2/abc => 404 Not found
  • domain/string20 => 200 Found
  • domain/string20/ => 200 Found
  • domain/string20/abc => 200 Found

I find this more clear, because I don't have to think in terms of exclusions. Rather, I can think of matching exactly what I want to forbid, then letting Laravel react to everything else (fail open policy). This may not meet your design criteria, but I do believe it results in clearer code.

Also, the code is more performant. ?! has to backtrack, which is by definition more expensive than forward matching.

I don't have a Laravel environment to hand, but I'll hazard a guess as to why your attempts didn't work. Laravel uses Symfony Router, which does not support lookarounds on slugs. IIRC, when a lookaround's detected, Symfony applies the lookaround to the entire URL, not the slug you've bound the pattern to. This messes with the developer's idea of how anchors (^, $) and greedy (*) meta characters work. This can lead to a bad experience in trying to get it to work, since the developer's operating under one assumption but the underlying libraries operating on another.


* Full disclosure, I wrote this for Lumen then mentally converted it to Laravel format. It's possible there are some translation errors. Here's the original Lumen:

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


来源:https://stackoverflow.com/questions/59549929/laravel-regex-route-match-everything-but-not-exactly-one-or-more-word

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!