Laravel 5.5 在路由名称螺丝中使用 env 字符串 Route::has()

Laravel 5.5 use of env string in route name screws Route::has()

出于绝对测试目的(这可能不是我将在我的代码中保留的一致行为)我尝试使用带有 .env 文件的自定义管理面板路由集(因为它似乎与保护整个事情相关,但不再那么确定了)。

因为我希望访问该站点的人可以在来宾模式下看到管理面板,所以我认为设置一个重新路由中间件会很酷,它只需在 .env 设置管理路由之后放置一个来宾。试图到达 /admin/jobs 的来宾将在 /admin/guest/jobs 以较低的权限控制器结束。

下面的代码在没有 .env 东西的情况下工作正常。

在\Route\web中是这样设置的。php

Route::namespace('Admin')->middleware('auth')->group(function () {
    $adminRoute = config('app.admin-route');
    $adminRoute = (preg_match('/[\/].*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;

    Route::middleware('isadmin')->group(function () use ($adminRoute) {
        Route::get($adminRoute, 'AdminController@index')->name('adminPanel');
        Route::get($adminRoute . '/test', function () {
            echo 'test';
        });
    });
    Route::get($adminRoute . '/guest/{where?}', 'AdminController@guest')->where('where', '.*')->name('adminAsGuest');
});

而对应的是Admin中间件:

public function handle($request, Closure $next)
{
    $adminRoute = config('app.admin-route');
    $adminRoute = (preg_match('/\/.*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;
    // check if authentified
    if (Auth::check())
    {
        // check if admin
        if (Auth::user()->role == 1) {
            return $next($request);
        }
        else
        {
            $route = "/".$request->route()->uri;
            // check if route exists
            if (Route::has($route)) {
                // trim the route after admin and puts a guest inside
                $redirect = preg_replace("/\".$adminRoute."/", "", $route);
                return redirect($adminRoute . '\/guest/' . $redirect);
            }
            else {
                // if it doesn't, let it go to the laravel error
                return $next($request);
            }
        }

    }

    // if auth middleware was used on same route, won't go there anyways
    // if not, redirect to root route
    return redirect('/');
}

我在那里挣扎,因为只要我使用 env("APP_ADMIN_ROUTE") 作为管理路由的根,Route::has($route) 就不会正确触发。

这是我在 if (Route::has($route))

之前的调试输出
Auth::check()
    true
Auth::user()->role
    0
$route
    "/admin/test"
Route::has($route);
    false


vagrant@ubuntu-xenial:/var/www/html$ wphp artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------------+
| Domain | Method   | URI                    | Name             | Action                                                                 | Middleware       |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------------+
|        | GET|HEAD | /                      |                  | App\Http\Controllers\WelcomeController@index                           | web              |
|        | GET|HEAD | admin                  | adminPanel       | App\Http\Controllers\Admin\AdminController@index                       | web,auth,isadmin |
|        | GET|HEAD | admin/guest/{where?}   | adminAsGuest     | App\Http\Controllers\Admin\AdminController@guest                       | web,auth         |
|        | GET|HEAD | admin/test             |                  | Closure                                                                | web,auth,isadmin |

我知道选择管理控制器肯定会更加一致,我会在其中不断跟踪连接的用户的角色,并授予或不授予修改权限。但我真的很想知道是否有任何可能的方法可以像我在这里尝试的那样工作。

我已经看到 Zizaco/entrust 并且它在更简单的方法上肯定会很好用,如果我当前的问题没有肯定的答案,这就是我的下一步 :)

这是我在 Whosebug 上的第一个问题。我对那个精确的东西进行了很好的搜索但没有成功。如果我在某处遗漏了明显的答案,我深表歉意。

编辑:在 Joel Hinz 发表评论后更新了代码。

// \Config\app.php
'admin-route' => env('APP_ADMIN_ROUTE', 'admin'),

通常最好让配置文件读取 .env 变量,然后从配置文件中加载您实际需要的设置。这样,它们就可以被缓存,而且您几乎总是知道它们已经被正确读取,而不必想知道它们在哪里可以工作,在哪里不能工作。

在你的情况下,像这样的东西就足够了:

// in .env
APP_ADMIN_ROUTE=something

// in e.g. config/app.php
'admin-route' => env('APP_ADMIN_ROUTE');

// in the middleware
$adminRoute = config('app.admin-route');

我不能 100% 确定这确实是您的问题,但值得一试 - 即使它不起作用,它仍然是最佳做法。 :)

好吧,第一个问题,第一个答案,看来我没有探索所有的可能性。

问题不是在路由 URI 定义中使用变量,而是 Route::has() 的不当使用。这仅适用于路由名称,不适用于路由 URI。根据 https://laracasts.com/discuss/channels/general-discussion/check-if-route-exists,检查路由是否存在的正确方法是使用 Route::checkRoutes->match($testValue) 和 Illuminate\Http\Request 的 $testValue 成员。

Route::checkRoutes->match($testValue) 将 return 具有第一个匹配项的 Illuminate\Routing\Route 实例,如果没有匹配项则为空值。

在中间件 handle() 上下文中,它可以这样表达:

$route = "/" . $request->route()->uri;
// Shape your test route URI in a request
$testValue = $request->create($route);
// check if route exists
if (Route::getRoutes()->match($testValue)) {
    // statements here
}

所以,正确的中间件完整代码是:

public function handle($request, Closure $next)
{
    $adminRoute = config('app.admin-route');
    $adminRoute = (preg_match('/\/.*/', $adminRoute)) ? $adminRoute : '/' . $adminRoute;
    // check if authentified
    if (Auth::check())
    {
        // check if admin
        if (Auth::user()->role == 1) {
            return $next($request);
        }
        else
        {
            $route = "/" . $request->route()->uri;
            $nreq = $request->create($route);
            // check if route exists
            if (Route::getRoutes()->match($nreq)) {
                // trim the route after admin and puts a guest inside
                $redirect = preg_replace("/\".$adminRoute."/", "", $route);
                return redirect($adminRoute.'/guest' . $redirect);
            }
            else {
                // if it doesn't, let it go to the laravel error
                return $next($request);
            }
        }

    }

    // if auth middleware was used on same route, won't go there anyways
    // if not, redirect to root route
    return redirect('/');
}

是什么让我认为这只是无用的代码块,因为中间件只会触发现有的 /admin/something 路由 URI,测试路由是否存在没有意义。

好吧,无论如何,感谢所有花时间阅读这篇文章的人,回头见。