Framework: Cached routes not getting loaded

Created on 17 Mar 2020  路  8Comments  路  Source: laravel/framework

  • Laravel Version: 7.2.0
  • PHP Version: 7.4.3

Description:

When caching routes they become unusable. I've tried tracking down the issue but cannot find the culprit.
I've debugged down the rabbit hole, but can't figure out why the RouteCollection isn't populated whilst being loaded from cache.

Route is listed fine in artisan route:list

a route:list | grep reports/source | | GET|HEAD | reports/source | reports.generated::h6iLoxUpb7OSO9T1 | App\Http\Controllers\ReportController@loadSource | web,auth,App\Http\Middleware\TransformCommaStringToArray

But once it tries to load the route by name it fails as $this->routes nameList is empty. But getRoutes() can see them fine.

Screenshot 2020-03-17 at 23 09 51

The error is also misleading, telling me that POST and DELETE are valid though they aren't?

MethodNotAllowedHttpException
HTTP 405 Method Not Allowed
The GET method is not supported for this route. Supported methods: HEAD, POST, DELETE.

Without cache the match method finds the route instantly
Screenshot 2020-03-17 at 23 16 50

bug

All 8 comments

Facing same issue

Could you post the code that define the route (the /routes/web.php file) and the cached file /bootstrap/cache/routes-v7.php, please. Want to try to replicate the bug.
Is there still the old cache file?

It has been two days since release and i have not seen any comments from Laravel team.
I am happy to provide a sample laravel repo to debug.

Here is the RouteServiceProvider code

Route::middleware(['web'])
            ->as('admin.')
            ->domain(config('domains.admin.domain'))
            ->namespace($this->namespace.'\Admin')
            ->group(base_path('routes/web.php'));

Routes in web.php

Route::redirect('/', 'login', 302);

Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
// Notice that this route does not have a name
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');

routes-v7.php content

<?php

/*
|--------------------------------------------------------------------------
| Load The Cached Routes
|--------------------------------------------------------------------------
|
| Here we will decode and unserialize the RouteCollection instance that
| holds all of the route information for an application. This allows
| us to instantaneously load the entire route map into the router.
|
*/

app('router')->setCompiledRoutes(
    array (
  'compiled' => 
  array (
    0 => true,
    1 => 
    array (
      '/' => 
      array (
        0 => 
        array (
          0 => 
          array (
            '_route' => 'admin.',
          ),
          1 => 'admin.example.test',
          2 => 
          array (
            'GET' => 0,
            'HEAD' => 1,
            'POST' => 2,
            'PUT' => 3,
            'PATCH' => 4,
            'DELETE' => 5,
            'OPTIONS' => 6,
          ),
          3 => NULL,
          4 => false,
          5 => false,
          6 => NULL,
        ),
      ),
      '/login' => 
      array (
        0 => 
        array (
          0 => 
          array (
            '_route' => 'admin.login',
          ),
          1 => 'admin.example.test',
          2 => 
          array (
            'GET' => 0,
            'HEAD' => 1,
          ),
          3 => NULL,
          4 => false,
          5 => false,
          6 => NULL,
        ),
        1 => 
        array (
          0 => 
          array (
            '_route' => 'generated::7Z6OFHMNFX8pu9Wc',
          ),
          1 => 'admin.example.test',
          2 => 
          array (
            'POST' => 0,
          ),
          3 => NULL,
          4 => false,
          5 => false,
          6 => NULL,
        ),
      ),
      '/logout' => 
      array (
        0 => 
        array (
          0 => 
          array (
            '_route' => 'admin.logout',
          ),
          1 => 'admin.example.test',
          2 => 
          array (
            'POST' => 0,
          ),
          3 => NULL,
          4 => false,
          5 => false,
          6 => NULL,
        ),
      ),
    ),
    2 => 
    array (
    ),
    3 => 
    array (
    ),
    4 => NULL,
  ),
  'attributes' => 
  array (
    'admin.' => 
    array (
      'methods' => 
      array (
        0 => 'GET',
        1 => 'HEAD',
        2 => 'POST',
        3 => 'PUT',
        4 => 'PATCH',
        5 => 'DELETE',
        6 => 'OPTIONS',
      ),
      'uri' => '/',
      'action' => 
      array (
        'middleware' => 
        array (
          0 => 'web',
        ),
        'domain' => 'admin.example.test',
        'uses' => '\\Illuminate\\Routing\\RedirectController@__invoke',
        'controller' => '\\Illuminate\\Routing\\RedirectController',
        'as' => 'admin.',
        'namespace' => 'App\\Http\\Controllers\\Admin',
        'prefix' => NULL,
        'where' => 
        array (
        ),
      ),
      'fallback' => false,
      'defaults' => 
      array (
        'destination' => 'login',
        'status' => 302,
      ),
      'wheres' => 
      array (
      ),
      'bindingFields' => 
      array (
      ),
    ),
    'admin.login' => 
    array (
      'methods' => 
      array (
        0 => 'GET',
        1 => 'HEAD',
      ),
      'uri' => 'login',
      'action' => 
      array (
        'middleware' => 
        array (
          0 => 'web',
        ),
        'domain' => 'admin.example.test',
        'uses' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@showLoginForm',
        'controller' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@showLoginForm',
        'as' => 'admin.login',
        'namespace' => 'App\\Http\\Controllers\\Admin',
        'prefix' => NULL,
        'where' => 
        array (
        ),
      ),
      'fallback' => false,
      'defaults' => 
      array (
      ),
      'wheres' => 
      array (
      ),
      'bindingFields' => 
      array (
      ),
    ),
    'admin.generated::7Z6OFHMNFX8pu9Wc' => 
    array (
      'methods' => 
      array (
        0 => 'POST',
      ),
      'uri' => 'login',
      'action' => 
      array (
        'middleware' => 
        array (
          0 => 'web',
        ),
        'domain' => 'admin.example.test',
        'uses' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@login',
        'controller' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@login',
        'as' => 'admin.generated::7Z6OFHMNFX8pu9Wc',
        'namespace' => 'App\\Http\\Controllers\\Admin',
        'prefix' => NULL,
        'where' => 
        array (
        ),
      ),
      'fallback' => false,
      'defaults' => 
      array (
      ),
      'wheres' => 
      array (
      ),
      'bindingFields' => 
      array (
      ),
    ),
    'admin.logout' => 
    array (
      'methods' => 
      array (
        0 => 'POST',
      ),
      'uri' => 'logout',
      'action' => 
      array (
        'middleware' => 
        array (
          0 => 'web',
        ),
        'domain' => 'admin.example.test',
        'uses' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@logout',
        'controller' => 'App\\Http\\Controllers\\Admin\\Auth\\LoginController@logout',
        'as' => 'admin.logout',
        'namespace' => 'App\\Http\\Controllers\\Admin',
        'prefix' => NULL,
        'where' => 
        array (
        ),
      ),
      'fallback' => false,
      'defaults' => 
      array (
      ),
      'wheres' => 
      array (
      ),
      'bindingFields' => 
      array (
      ),
    ),
  ),
)
);

Found out every route must have a name to make cache work. Not 100% sure.
Otherwise i am getting this error on login action

The POST method is not supported for this route. Supported methods: GET, HEAD.

Found the issue, will explain it and maybe @driesvints can fix the black magic behind it.

It comes down to us setting a name on the group
Route::name('lookup.')->prefix('lookup')->group(base_path('routes/web/lookup.php'));

In the group I have 2 urls now.

Route::get('/fake-news', [LookupController::class, 'source']);
Route::get('/source/{type}', [LookupController::class, 'source']);
a route:list                                                                                                                                                                                                                                                                                                    +--------+----------+----------------------+---------+--------------------------------------------------+------------+
| Domain | Method   | URI                  | Name    | Action                                           | Middleware |
+--------+----------+----------------------+---------+--------------------------------------------------+------------+
|        | GET|HEAD | lookup/fake-news     | lookup. | App\Http\Controllers\Api\LookupController@source | web        |
|        | GET|HEAD | lookup/source/{type} | lookup. | App\Http\Controllers\Api\LookupController@source | web        |
+--------+----------+----------------------+---------+--------------------------------------------------+------------+

cached

a route:list                                                                                                                                                                                                                                                                                                   
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+
| Domain | Method   | URI                  | Name                               | Action                                           | Middleware |
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+
|        | GET|HEAD | lookup/fake-news     | lookup.                            | App\Http\Controllers\Api\LookupController@source | web        |
|        | GET|HEAD | lookup/source/{type} | lookup.generated::rsfnRZ32JW2CM4gj | App\Http\Controllers\Api\LookupController@source | web        |
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+

This then gives the 405 error. But if I switch the position of the routes, it gives a 200...

Route::get('/source/{type}', [LookupController::class, 'source']);
Route::get('/fake-news', [LookupController::class, 'source']);

cached

a route:list                                                                                                                                                                                                                                                                                                    
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+
| Domain | Method   | URI                  | Name                               | Action                                           | Middleware |
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+
|        | GET|HEAD | lookup/fake-news     | lookup.generated::KIbM69Jcmm1a7iAa | App\Http\Controllers\Api\LookupController@source | web        |
|        | GET|HEAD | lookup/source/{type} | lookup.                            | App\Http\Controllers\Api\LookupController@source | web        |
+--------+----------+----------------------+------------------------------------+--------------------------------------------------+------------+

Final solution is also to remove the name from group then everything works. But I still see it as a bug as it all works pre-cache.

Actually removing name from resource group isn't the way to go as we have same names in different namespaces, and since resources add a name automatically this will collide, so there needs to be a proper fix for this.

Route names need to be unique from now on. Please see https://laravel.com/docs/7.x/upgrade#unique-route-names

@danijelk The error seems to be related to a change we made for route group naming. We'll check in on this.

This then gives the 405 error. But if I switch the position of the routes, it gives a 200...

WHAT gives a 405 error?

This then gives the 405 error. But if I switch the position of the routes, it gives a 200...

WHAT gives a 405 error?

Sending a GET request for route 'reports/source'.

But I made a clearer case https://github.com/laravel/framework/issues/32015#issuecomment-601147924 with just 2 routes in one group, easier to understand and replicate.

We've released a fix for this. Thanks for reporting all.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lzp819739483 picture lzp819739483  路  3Comments

iivanov2 picture iivanov2  路  3Comments

Anahkiasen picture Anahkiasen  路  3Comments

JamborJan picture JamborJan  路  3Comments

YannPl picture YannPl  路  3Comments