从苗条中间件重定向的正确方法

Proper way to redirect from slim middleware

目标是将用户从中间件重定向到登录页面。 中间件是从带有以下行的苗条路由组调用的:

$this->get( '/profile', ProfileController::class . ':index' )->add( new RequireLogin() );

这是中间件

<?php

namespace Rib\Src\MiddleWares;


use Rib\Src\Services\FlashMessages;
use Slim\Http\Request;
use Slim\Http\Response;

class RequireLogin
{


    /**
     * Example middleware invokable class
     *
     * @param  \Psr\Http\Message\ServerRequestInterface $request PSR7 request
     * @param  \Psr\Http\Message\ResponseInterface $response PSR7 response
     * @param  callable $next Next middleware
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function __invoke( $request, $response, $next )
    {

        if ( ! isset( $_SESSION[ 'id' ] ) ) {
            FlashMessages::flashIt( 'message', "The page you tried to access requires an active session. Please log back in." );
            header( 'Location: /user/login' ); # THIS LINE
            exit; # AND THIS ONE
        }



        $response = $next( $request, $response );

        return $response;
    }

}

我的目标是当中间件检测到用户当前未登录时,将用户重定向到登录页面。 您可以看到我当前的方式是错误的:我使用重定向后跟退出。

如何用正确的 "slim" 方式替换这两行?

Response有个好用的方法withRedirect(),你可以这样使用:

<?php

if (!isset($_SESSION[ 'id' ])) {
    FlashMessages::flashIt( 'message', "The page you tried to access requires an active session. Please log back in." );
    $url = '/user/login';
    return $response->withRedirect($url);
} else {
    return $next($request, $response);
}

但是仍然存在一个问题:您正在硬编码 URL 您希望将未经身份验证的用户重定向到的位置。考虑通过它的名称获取 URL(你可以在声明路由时给出):

<?php

// Add unique name to the route so you can 
// refer to it later in the code (this is what we're going to do now)
$app->get('/user/login', YourController::class)->setName('userLoginPage');

现在您可以通过名称引用该路由,而不是中间件中的 URL 本身。为此,您需要注入 Slim Router 对象:

<?php

namespace Rib\Src\MiddleWares;


use Rib\Src\Services\FlashMessages;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Router;

class RequireLogin
{
    /**
     * Object constructor
     * 
     * Inject Slim Router here, so you can access route names.
     * 
     * @param Router $router 
     */
    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    /**
     * Example middleware invokable class
     *
     * @param  \Psr\Http\Message\ServerRequestInterface $request PSR7 request
     * @param  \Psr\Http\Message\ResponseInterface $response PSR7 response
     * @param  callable $next Next middleware
     *
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function __invoke( $request, $response, $next )
    {

        if ( ! isset( $_SESSION[ 'id' ] ) ) {
            FlashMessages::flashIt( 'message', "The page you tried to access requires an active session. Please log back in." );
            // Get url by name: this is more flexible than hardcoding URL
            $url = $this->router->pathFor('userLoginPage') . ' #forbidden';
            return $response->withRedirect($url);
        } else {
            return $next( $request, $response );
        }
    }

}

这里的好处是,当您更改用户登录页面的实际 URL 时,您不必在中间件中更改它,因为它是通过 $router->pathFor() 方法访问的。

我会说这是非常苗条的好方法。 )