Welcome Guest, Not a member yet? Register   Sign In
'Improved' Auto Router exception skips filter for 404 controller

I'm currently upgrading from 4.1.4 to 4.3.1 and struggling heavily with the new 'improved' routing. My current blocking point is the following.
We have a custom 404 handler controller method that relies on a filter (set to run before every controller, thus a potentially nonexistent as well) to tell the access level of the currently logged in user, as we want to display a different error (404) page based on the access level of the user (or whether there is a user at all). In 4.1.4, the filters ran before the PageNotFoundException was thrown:
CodeIgniter.php:768, CodeIgniter\CodeIgniter->startController()
CodeIgniter.php:386, CodeIgniter\CodeIgniter->handleRequest()
CodeIgniter.php:317, CodeIgniter\CodeIgniter->run()
index.php:37, {main}()

In 4.3.1's improved auto router, however, this exception is thrown much earlier, even before the filters run:
AutoRouterImproved.php:196, CodeIgniter\Router\AutoRouterImproved->protectDefinedRoutes()
AutoRouterImproved.php:172, CodeIgniter\Router\AutoRouterImproved->getRoute()
Router.php:506, CodeIgniter\Router\Router->autoRoute()
Router.php:205, CodeIgniter\Router\Router->handle()
CodeIgniter.php:815, CodeIgniter\CodeIgniter->tryToRouteIt()
CodeIgniter.php:449, CodeIgniter\CodeIgniter->handleRequest()
CodeIgniter.php:368, CodeIgniter\CodeIgniter->run()
index.php:67, {main}()

In 4.1.4, tryToRouteIt() didn't throw, but startController() did; 'before' filter execution is just in-between.
Behavior is the same (exception is thrown at another line but still within tryToRouteIt()) regardless of whether the controller specified by the URI exists, but the method is missing, or it is completely missing.
Is this intended behavior? If yes, is there suggested solution, as I cannot find this issue covered in the upgrade/release notes, while it is clearly breaking earlier behavior.

(This post was last modified: 01-30-2023, 10:30 PM by kenjis.)

My understanding is that if there is no controller method to execute, filters will not run.
So it is the intended behavior.

I see that you have a very specific config, but could you please show us a minimal config/sample code to reproduce it?

And then, if you change to Auto Routing (legacy), does the behavior change?

Thanks for your help!

I uploaded a minimal demo for the issue: https://github.com/Saddalim/ci4_autoroute_filter

IRL, we would need the results of MagicFilter::before in Home::error404. Please ignore the ugly static, it's for the easy demo...
If you try to load a nonexisting controller, you'll see that the filter runs with the legacy router, but if you switch $autoRoutesImproved to true, it does not.

As a quick workaround in the original app, I moved functionality of the filter to a library class and called it from both the filter and and 404 error handler method (or alternatively I could have used a trait I believe), but I would prefer a less smelly solution.

(This post was last modified: 01-31-2023, 05:00 PM by kenjis.)

Thank you for the demo.

When I navigate to http://localhost:8080/404:
- default: filter ran? : false
- $autoRoutesImproved = true: filter ran? : false

The both results are the same.

When I navigate to http://localhost:8080/xxx,  I see "filter ran? : true" with auto routing legacy.

Okay, I got the situation.

As you said, when auto routing legacy is enabled, global filters are executed
if the URI segment for the controller name is valid as a classname.
It does not matter whether you use 404 override or not.

But as I wrote, if we navigate to http://localhost:8080/404, "404" is not a valid
classname. So filters are not executed.

I think this is a bug. At least they are not consistent.
I created an issue:

I think controller filters should be executed only when the route exists.

If you want to apply filters in your 404 override method, you can call them manually.
I don't see the need to run filters on 404 routes.

--- a/app/Controllers/Home.php
+++ b/app/Controllers/Home.php
@@ -2,6 +2,8 @@

namespace App\Controllers;

+use App\Filters\MagicFilter;
class Home extends BaseController
    public static bool $filterRan = false;
@@ -13,6 +15,9 @@ class Home extends BaseController

    public function error404()
+        $filter = new MagicFilter();
+        $filter->before($this->request);
        return 'error page<br>filter ran? : ' . (self::$filterRan ? 'true' : 'false');

(This post was last modified: 02-01-2023, 06:25 AM by saddalim.)

Great, thanks. Seems like we built on a bug rather than a feature Angel 
I would think twice about modifying the legacy router, though. One would not expect change of behavior from a class that is born to reproduce the original, but this is a philosophical question rather than a technical one Smile

Theme © iAndrew 2016 - Forum software by © MyBB