【问题标题】:Apply Middleware to all routes except `setup/*` in Laravel 5.4在 Laravel 5.4 中将中间件应用于除 `setup/*` 之外的所有路由
【发布时间】:2017-08-25 09:33:32
【问题描述】:

我正在我的 Laravel 应用程序中试验中间件。我目前已将它设置为在经过身份验证的用户的每条路由上运行,但是,我希望它忽略任何以 setup URI 开头的请求。

这是我的CheckOnboarding 中间件方法的样子:

public function handle($request, Closure $next)
{
    /** 
    * Check to see if the user has completed the onboarding, if not redirect.
    * Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
    */
    if ($request->user()->onboarding_complete == false && $request->path() != 'setup') {
        return redirect('setup');
    } else {
        return $next($request);
    }
}

这在我的路线中使用如下:

Route::group(['middleware' => ['auth','checkOnboarding']], function () {
    Route::get('/home', 'HomeController@index');
    Route::get('/account', 'AccountController@index');

    Route::group(['prefix' => 'setup'], function () {
        Route::get('/', 'OnboardingController@index')->name('setup');
        Route::post('/settings', 'SettingsController@store');
    }); 
});

现在,如果我转到 /home/account,我会按照您的预期重定向到 /setup。这最初导致了重定向循环错误,因此& $request->path() != 'setup' 在中间件中。

我觉得这是一种非常笨拙的方式,显然与 setup 之后的任何东西都不匹配,比如我创建的 setup/settings 路由。

有没有更好的方法让这个中间件在用户的所有路由上运行,同时还设置一些应该免除此检查的路由?

【问题讨论】:

    标签: php laravel laravel-5 laravel-5.4 laravel-middleware


    【解决方案1】:

    你不希望中间件运行的路由,只需将它们放在函数之外:

    //here register routes on which you dont want the middleware: checkOnboarding
    Route::group(['middleware' => ['auth','checkOnboarding']], function () {
         //routes on which you want the middleware
    });
    

    【讨论】:

      【解决方案2】:

      您所做的并没有错,但是,我建议您将路线组分开,即:

      Route::group(['middleware' => ['auth', 'checkOnboarding']], function () {
          Route::get('/home', 'HomeController@index');
          Route::get('/account', 'AccountController@index');
      });
      
      Route::group(['prefix' => 'setup', 'middleware' => 'auth'], function () {
          Route::get('/', 'OnboardingController@index')->name('setup');
          Route::post('/settings', 'SettingsController@store');
      });
      

      或者,为您的身份验证设置一个父组:

      Route::group(['middleware' => 'auth'], function () {
      
          Route::group(['middleware' => 'checkOnboarding'], function () {
              Route::get('/home', 'HomeController@index');
              Route::get('/account', 'AccountController@index');
          });
      
          Route::group(['prefix' => 'setup'], function () {
              Route::get('/', 'OnboardingController@index')->name('setup');
              Route::post('/settings', 'SettingsController@store');
          });
      });
      

      这也意味着您可以删除中间件中的额外条件:

      /**
       * Check to see if the user has completed the onboarding, if not redirect.
       * Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
       */
      return $request->user()->onboarding_complete ? $next($request) : redirect('setup');
      

      希望这会有所帮助!

      【讨论】:

        【解决方案3】:

        您可以使用 Controller 类来实现这一点,效果非常好。

        如果您在 HTTP/Controllers/Controller.php 中创建 __construct 函数,那么您可以声明中间件以在每个控制器操作上运行,甚至可以根据需要声明异常。

        class Controller extends BaseController
        {
          use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
          public function __construct(){
            $this->middleware('auth',['except' => ['login','setup','setupSomethingElse']]);
          }
        }
        

        注意不要将任何标准的索引、存储、更新、销毁函数放在异常中,否则会引发潜在的安全问题。

        【讨论】:

        • 对我来说,这比使用路由更清晰、更具声明性。当然,这只是个人品味和经验的问题,但我投票给这个。
        【解决方案4】:

        有两种方法可以解决这个问题

        1. 尝试在路线文件web.php or api.php 中筛选您的路线
        2. 跳过middleware中的路线

        如果是全局中间件(你想在所有路由之前运行的中间件),你应该在中间件中跳过路由。

        例如:

        //add an array of routes to skip santize check
        protected $openRoutes = [
            'setup/*',
        ];
        
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if(!in_array($request->path(), $this->openRoutes)){
               //middleware code or call of function
            }       
        
            return $next($request);
        }
        

        对于其他中间件,您可以轻松跳过路由文件并根据您的中间件对路由进行分组。

        例如:

        Route::group(['middleware' => 'checkOnboarding'], function () {
                Route::get('/home', 'HomeController@index');
                Route::get('/account', 'AccountController@index');
            });
        
        Route::group(['prefix' => 'setup'], function () {
            Route::get('/', 'OnboardingController@index')->name('setup');
            Route::post('/settings', 'SettingsController@store');
        });
        

        【讨论】:

          【解决方案5】:

          从 Laravel 7.7 开始,您可以像这样使用excluded_middleware

          Route::group(['middleware' => ['auth','checkOnboarding']], function () {
              Route::get('/home', 'HomeController@index');
              Route::get('/account', 'AccountController@index');
          
              Route::group([
                'prefix' => 'setup',
                'excluded_middleware' => ['checkOnboarding'],
          ], function () {
                  Route::get('/', 'OnboardingController@index')->name('setup');
                  Route::post('/settings', 'SettingsController@store');
              }); 
          });
          

          【讨论】:

            【解决方案6】:

            在 Laravel 8.x 中,您还可以使用 withoutMiddleware() 方法来排除到组中间件的一个或多个路由

            Route::middleware('auth')->group(function () {
                Route::get('/edit/{id}',[ProgramController::class, 'edit'])->name('edit');
            
                Route::get('/public', [ProgramController::class, 'public'])
                      ->name('public')->withoutMiddleware(['auth']);
            });
            

            查看官方文档:Here

            【讨论】:

            • 这对于较新的版本很方便 :) 您可能想快速编辑您的答案,以便链接不是代码块的一部分
            猜你喜欢
            • 2021-07-03
            • 2019-06-09
            • 2017-11-29
            • 2020-07-23
            • 2019-10-05
            • 1970-01-01
            • 1970-01-01
            • 2018-07-20
            • 2014-07-31
            相关资源
            最近更新 更多