PrettyUrls Strict Parsing Dilemma

Firstly, I’m not necessarily looking for ‘solution,’ although a better one than what I’ve come

up with would be nice. I was juggling whether I should bring this up on GitHub but decided I’d

post here first.

What I want is to have some way to determine that an error is caused by a requested route not

being found in UrlManager’s rules. Currently, \yii\web\UrlManager->parseRequest() returns False

if the rule is not found, this translates to a very generic NotFoundHttpException. What I was

debating was whether I make a request to change the exception that is thrown, perhaps something

like InvalidRouteException. The problem I see with this is it may break applications that rely

on the NotFoundHttpException being thrown here, and semantically "Not Found" does make sense,

but doesn’t provide much detail.

Relevant code sections are below for convenience.


namespace yii\web;

class Request extends \yii\base\Request {

    public function resolve() {

        $result = Yii::$app->getUrlManager()->parseRequest($this);

        if ($result !== false) {

            [...]

        } else {

            throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));

        }

    }

}


namespace yii\web;

class UrlManager extends Component {

    parseRequest() {

        if ($this->enablePrettyUrl) {

            $pathInfo = $request->getPathInfo();

            /* @var $rule UrlRule */

            foreach ($this->rules as $rule) {

                if (($result = $rule->parseRequest($this, $request)) !== false) {

                    return $result;

                }

            }


            if ($this->enableStrictParsing) {

                return false;

            }

            [...]

        }

        [...]

    }

}

My use case and included work around for this is is as follows. Any suggests would be appreciated.


config.php

// Only the UrlManager and ErrorHandler components are here for simplicity


[

    'components' => [

        'errorHandler' => [

            'errorAction' => 'site/error',

        ],

        'urlManager' => [

            'showScriptName' => false,

            'enablePrettyUrl' => true,

            'enableStrictParsing' => true,

            'rules' => [

                'site/post/<id:\d+>' => 'site/post',

            ],

         ],

    ],

]


namespace app\controllers;

class SiteController extends \yii\web\Controller {

    /**

     * View a post.

     */

    public function actionPost() {

        $validPosts [1, 2, 4, 8, 16];

        $postId = intval(\Yii::$app->request->get('id', 0));


        if (! in_array($postId, $validPosts)) {

            throw new \yii\web\NotFoundHttpException('Post cannot be found');

        }


        return $this->render('post', ['postId' => $postId]);

    }

    /**

     * Display an error to the user.

     */

    public function actionError() {

        $exception = \Yii::$app->errorHandler->exception;


        if ($exception instanceof \yii\web\NotFoundHttpException

            && $exception->file === '/web/site/vendor/yiisoft/yii2/web/Request.php') {

            // Probably a bad route.

            return $this->render('badUrl');

        } else {

            return $this->render('error');

        }

    }

}

While this ‘works’, I don’t really think it’s the best solution. Any ideas would be great or

let me know if I should create an issue for this.

Hi Novies,

This is what I would try first (not tested):

[list=1][][size=2]Extend UrlManager (naming to something like AppUrlManager);[/size][][size=2]Update configs to use your new AppUrlManager instead UrlManager;[/size][][size=2]Create a new custom exception, maybe InvalidRouteException, as you said;[/size][][size=2]Override the relevant parent method of AppUrlManager or wrap it in a catch block to get the exception being thrown and throw a new one (InvalidRouteException);[/size][/list]

Yeah, that works better than what I had. Thanks.

You are welcome!