Skip to content

Conversation

@vlastavesely
Copy link
Contributor

When Application::createInitialRequest() ends with an exception (missing presenter), Application::processException() is called before Application::processRequest(). This is problem for processException() tries to read property Application::$requests that is, however, set later by processRequest(). So, even if application got request from router, ErrorPresenter gots NULL instead of that request.

For this one specific case it should be set manually.

@TomasVotruba
Copy link

Could you please add test for that as well?

@vlastavesely
Copy link
Contributor Author

Test added.

@dg
Copy link
Member

dg commented Oct 17, 2016

This should be fixed in different way, because createInitialRequest should not sometimes change $requests as side effect.

@TomasVotruba
Copy link

Any suggestions?

@vlastavesely
Copy link
Contributor Author

I'm thinking about that. I've found two another solutions but they are not as direct as the first one.

One is to call Router::match() before Application::createInitialRequest(), save it into variable and pass it as second parameter into Application::processException() where it can be used if end($this->requests) is NULL.

    $request = $this->router->match($this->httpRequest);
    $this->processRequest($this->createInitialRequest($request));

    ...

    $this->processException($e, $request); // in Application::run(), if exception thrown

    ...

    $args = ['exception' => $e, 'request' => end($this->requests) ?: $request]; // in processException()

The second solution is simply to call Router::match() again if last response is NULL like that:

    $request = end($this->requests);
    if (!$request) {
        $request = $this->router->match($this->httpRequest);
    }
    $args = ['exception' => $e, 'request' => $request];

In the second solution there is unnecessary calling Route::match() again if createInitialReqiest() fails (we already had apprequest from router so why call it again?), first solution is more similar to my original suggestion but not as direct as that one.

@JanTvrdik
Copy link
Contributor

I think that you should store the request object inside BadRequestException and processException should get the request object from BadRequestException if available.

@vlastavesely
Copy link
Contributor Author

Updated. Thanks for suggestion!

}

/**
* @return Request
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@return Request|NULL

/** @var int */
protected $code = 404;

/** @var Request */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request|NULL

$this->request = $request;
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should always be two empty lines between methods

}

$args = ['exception' => $e, 'request' => end($this->requests) ?: NULL];
$args = ['exception' => $e, 'request' => end($this->requests) ?: $e->getRequest()];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that $this->requests can only be empty when BadRequestException was thrown which I'm not sure is something we can rely on.

What about rewriting the whole method like this?

if ($e instanceof BadRequestException) {
    $httpCode = $e->getCode() ?: 404;
    $lastRequest = $e->getRequest() ?: end($this->requests);

} else {
    $httpCode = 500;
    $lastRequest = end($this->requests);

    if ($this->httpResponse instanceof Nette\Http\Response) {
        $this->httpResponse->warnOnBuffer = FALSE;
    }
}

$this->httpResponse->setCode($httpCode);
$params = ['exception' => $e, 'request' => $lastRequest];

...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yes. If Router::match() fails, it can make problems. That check of type of $e is needed.

dg added a commit to dg/nette-application that referenced this pull request Oct 19, 2016
@dg
Copy link
Member

dg commented Oct 19, 2016

What about this? dg@022574b

@vlastavesely
Copy link
Contributor Author

It solves one problem but creates another. If no route matches, it ignores Application::$errorPresenter and shows Nette's default. Right now, I do not know where is problem, I'll check it later.

With today's solution in my PR it works correctly (in all tested cases there is custom ErrorPresenter).

@dg
Copy link
Member

dg commented Oct 19, 2016

fixed dg@0ca9097

dg added a commit to dg/nette-application that referenced this pull request Oct 19, 2016
@vlastavesely
Copy link
Contributor Author

throw $isForward ? new BadRequestException($e->getMessage(), 0, $e) : $e;

This line makes problem. It throws InvalidPresenterException, so error code is 500. I think that for missing presenter would be better 404. What about throw always BadRequestException?

@dg
Copy link
Member

dg commented Oct 19, 2016

Yes, I noticed it. dg@9678905

@vlastavesely
Copy link
Contributor Author

OK, now it seems to be working in all cases.

@dg dg closed this in 7b07c4e Oct 19, 2016
@TomasVotruba
Copy link

Great job, thank you guys

@vlastavesely vlastavesely deleted the apprequest-fix branch May 24, 2017 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants