redirect from Presenter always call action <default>

5 years ago

Václav Novotný
Member | 13
+
0
-

Hello,

I check user privileges in basePresenter and want conditionaly redirect to “Sign:in”:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    public function startup()
    {
        parent::startup();

        if (!$this->getUser()->isLoggedIn())
            $this->redirect('Sign:in');
    }
}

problem is with

$this->redirect('Sign:in')

which calls ‘Sign:default’.

When I use

$this->forward('Sign:in')

it works perfectly, but $this->redirect() always falls to action ‘default’.

My router has two routes, one is REST route and the second is basic route from Nette Sandbox, I don't thik, that problem lies here, but I'm not really sure.

class RouterFactory
{

    /**
     * @return \Nette\Application\IRouter
     */
    public function createRouter()
    {
        $router = new RouteList();
        $router[] = new CrudRoute('api/v1/<presenter>[/<id>/[<relation>[/<relationId>]]]', array(
            'module' => 'Api'
        ));
        $router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
        return $router;
    }

}

I work on localhost (WAMP, Nette 2.2.3), never before experienced such problem, do anyone have any idea?

Last edited by Václav Novotný (2014-11-03 11:29)

5 years ago

Milo
Nette Core | 1149
+
0
-

From what inherit your SignPresenter? If from BasePreseter, it is redirect loop.

5 years ago

Václav Novotný
Member | 13
+
0
-

@Milo that's true, but I wrote simple code to interpret my problem, in fact, I conditionally check presenterName in startup() method to prevent redirect loop

public function startup()
    {
        parent::startup();

        if (!$this->getUser()->isLoggedIn() && $this->getRequest()->getPresenterName() !== 'Sign')
            $this->redirect('Sign:in');
    }

5 years ago

Azathoth
Member | 506
+
0
-

I suggest you to make abstract SecuredPresenter or something like that which will have startup method with sign in check, instead of base presenter.
It is more clean solution and you do not have to check presenter name.

5 years ago

Václav Novotný
Member | 13
+
0
-

I'm really confusing by this. I have traced stack to Nette\Application\Routers\RouteList where is method constructUrl()

/**
     * Constructs absolute URL from Request object.
     * @return string|NULL
     */
    public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl)
    {
        if ($this->cachedRoutes === NULL) {
            // ...create $this->cachedRoutes;
        }

        if ($this->module) {
            // do somethig...
        }

        $presenter = strtolower($appRequest->getPresenterName());
        if (!isset($this->cachedRoutes[$presenter])) {
            $presenter = '*';
        }
// added die() here
die(var_dump($appRequest->getParameters()));
        foreach ($this->cachedRoutes[$presenter] as $route) {
            $url = $route->constructUrl($appRequest, $refUrl);
            if ($url !== NULL) {
// added die() here, when $url !== NULL
die(var_dump($appRequest->getParameters()));
                return $url;
            }
        }

        return NULL;
    }

the first

die(var_dump($appRequest->getParameters()));

outputs

array(2) { ["backlink"]=> string(5) "g7fdt" ["action"]=> string(2) "in" }

which is good ('action' => 'in'), but when I comment the first die(...) and run the app again to get $params when $url !== NULL, it outputs ('action' => 'default')

so the $route->constructUrl has to run twice. First time when $appRequest contains 'action' => 'in' but it ends without success (all $url === NULL), so it is called again (somewhere) with $appRequest containing 'action' => 'default' and this time it succeed to construct URL and do the redirection to ‘Sign:default’.

I'm confused, why it works, when I use $this->forward() and not for $this->redirect().

@Azathoth: the page is small community server for holding beer consumption ranks, reviews of different beers and simple accounting of payments for kegs, therefor all pages are secured, because we do not want our wifes to easily check our progress :) I implemented your advice so SignPresenter extends Nette\Application\UI\Presenter and all other presenters extends BasePresenter, where is isLoggedIn check. Thank you.

5 years ago

David Grudl
Nette Core | 6864
+
0
-

Forward() is not using router.

5 years ago

Václav Novotný
Member | 13
+
0
-

I've catch the problem. Obviously, it was in RouterFactory

class RouterFactory
{

    /**
     * @return \Nette\Application\IRouter
     */
    public function createRouter()
    {
        $router = new RouteList();
        $router[] = new CrudRoute('api/v1/<presenter>[/<id>/[<relation>[/<relationId>]]]', array(
            'module' => 'Api'
        ));
        $router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
        return $router;
    }

}

the reason, why I never experienced such weirdness before, is because I've never before used CrudRoute. If I comment that route out, everything works good. Don't know, why I did not try this in the first place…

So the problem is with CrudPresenter, which I've got from drahak/Restful.

At least I know where to dig.

5 years ago

Václav Novotný
Member | 13
+
0
-

OK, got it in Drahak\Restful\Application\Routes\ResourceRoute extends Nette\Application\Routers\Route implements Drahak\Restful\Application\IResourceRouter in method constructUrl() on line 142

It changed $appRequest with $appRequest->setParameters($params) in first if statement

if (count($this->actionDictionary) > 0) {
    $params = $appRequest->parameters;
    $params['action'] = 'default'; // so the request matches with route with action dictionary
    $appRequest->setParameters($params);
}

so I have cloned $appRequest for that purposes:

if (count($this->actionDictionary) > 0) {
    $appRequest = clone $appRequest;
    $params = $appRequest->getParameters();
    $params['action'] = 'default'; // so the request matches with route with action dictionary
    $appRequest->setParameters($params);
}

Maybe not the most elegant way, but it works now.