Router – Invalid Presenter Exception

d@rkWolf
Člen | 167
+
0
-

Dá se nějak vyřešit v routeru příchozí dotazy na neexistující soubory? Př.:

Nějaký stupidní robot(nebo bůh ví kdo) zavolá:
doména/admin.tar

A mě to vyhodí do logu:

Nette\Application\InvalidPresenterException
Cannot load presenter ‚Front:Admin:Error4xx‘, class ‚App\FrontModule\AdminModule\Presenters\Error4xxPresenter‘ was not found.

Z nějakého důvodu to vytvoří několik zanoření modulů a nenajde error presenter, logicky, protože tam dvojí zanoření modulů nemám.

V routeru mám:

        $router = new RouteList;

        $router->withModule('Admin')
               ->addRoute('admin/<presenter>[/<action>][/<id>][.html][.php]', 'Dashboard:default');

        $router->withModule('Front')
               ->addRoute('[<locale=en en>/]index.php', [
                   'locale'    => 'en',
                   'presenter' => 'Homepage',
                   'action'    => 'default',
                   'id'        => null,
               ], Route::ONE_WAY)
               ->addRoute('[<locale=en en>/]sitemap[s][!.xml]', 'Sitemap:default')

		... pár dalších specifičtějších rout ...

		 a končí to tou, která by měla být nejvíc obecná:

               ->addRoute('[<locale=en en>/][<presenter>[/<action>[/<id>]]][.html][.php]', [
                   'locale'    => 'en',
                   'presenter' => 'Homepage',
                   'action'    => 'default',
                   'id'        => null,
               ])
            ->end();

        return $router;

kvůli předchozí verzi tam směruju oneway z index.php a pro jistotu taky adresy s .html nebo .php na verzi bez toho

Mapping v configu:

application:
    errorPresenter: Error
    mapping:
        *: App\*Module\Presenters\*Presenter

Mám vytvořený AdminModule a FrontModule, v každém je v FrontModule/Presenters/Error4xxPresenter, samotný ErrorPresenter je v /Presenters mimo moduly.

To samé se děje třeba s u požadavků:

/admin/config.ini → class ‚App\AdminModule\ConfigModule\Presenters\Error4xxPresenter‘ was not found.
/admin/app.env → class ‚App\AdminModule\AppModule\Presenters\Error4xxPresenter‘ was not found.
/adminer.sql → class ‚App\FrontModule\AdminerModule\Presenters\Error4xxPresenter‘ was not found.

atd…atd…

Přepokládám, že tam mám buď něco špatně, nebo, že se to chování dá nějak upravit, ale nedaří se mi přijít na to jak? Na všechny tyhle dotazy by měl vypadnout 4xx presenter, ať už z Adminu(tam to ale přesměruje na přihlášení), nebo z Front-endu.

Marek Bartoš
Nette Blogger | 1264
+
0
-

A matchnulo ti to nějakou routu? Spíš si myslím, že ti to padá na error presenter nastavený v configu a ten chybně dělá forward() na neexistující presenter v modulu

m.brecher
Generous Backer | 864
+
0
-

@d@rkWolf

application:
errorPresenter: Error

Výjimka se přehodí nejprve na ErrorPresenter, který záleží jak máš udělaný. Ten by měl optimálně výjimku zpracovat a pro běžné 404 nelogovat, ale poslat chybovou stránku. Pro 500 pak chybu logovat – může to být něco závažného.

Tak jak to máš udělané tak přijde 404, ale ErrorPresenter z ní udělá 500 tím že mapuje neexistující Error4xxPresenter – udělá tam špatnou cestu.

Řešil bych to v ErrorPresenteru nastavením natvrdo cesty k existujícímu Error4xxPresenteru pro všechny moduly stejný.

d@rkWolf
Člen | 167
+
0
-

@MarekBartoš no tracy se tváří, že to matchlo tu poslední

@m.brecher to samozřejmě není možné, ten systém musí mít 4xx pro frontend(včetně dostupného menu atd., eventuelně search komponenty) a úplně jinou pro backend

v errorPresenteru to mám takto

    public function run(Nette\Application\Request $request): Nette\Application\IResponse
    {
        $exception = $request->getParameter('exception');

        if ($exception instanceof Nette\Application\BadRequestException) {
            $presenterName = $request->getParameter('request') ? $request->getParameter('request')->getPresenterName() : $request->getPresenterName();
            [$module, , $sep] = Nette\Application\Helpers::splitName($presenterName);

            // forward only to admin or front error4xx presenter
            if (!strstr($module, 'Admin')) {
                $module = ($sep === ':') ? 'Front' : 'Front:';
            }

            return new Responses\ForwardResponse($request->setPresenterName($module . $sep . 'Error4xx'));
        }

        $this->logger->log($exception, ILogger::EXCEPTION);

        return new Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse): void {
            if (preg_match('#^text/html(?:;|$)#', (string)$httpResponse->getHeader('Content-Type'))) {
                require __DIR__ . '/templates/Error/500.phtml';
            }
        });
    }

problém sou ty tečky, když chodí ty požadavky se jmény souborů, protože se to rozdělí na 2, vznikne modul-část před tečkou a presenter-část za tečkou.

PresenterName pro /adminer.sql je toto: Front:Adminer:Sql

  • Front je dobře, tam to potřebuju poslat, ale Adminer:Sql by být nemělo, to Sql by asi mělo být zahozené, nebo já nevím? nebo ty soubory nějak úplně odstínit, ale netuším jak? proto se právě ptám :-)

Nepřišel jsem na to, jak Routeru říct, že takto prostě ne-v podstatě jsem doufal, že to zařídí, když si tam dám ty – [.html][.php], ale ne, to není ono…

m.brecher
Generous Backer | 864
+
0
-

@d@rkWolf

Máš dva moduly, Admin se pozná tím, že začíná url za doménou admin, takže nejrychlejší řešení by mohlo být jednoduše identifikovat modul admin podle tohoto řetězce. Zkus na to jít nějak takhle:

final class ErrorPresenter implements Nette\Application\IPresenter
{
    use Nette\SmartObject;

    public function __construct(private ILogger $logger, private Nette\Http\Request $httpRequest)
    {}


    public function run(Nette\Application\Request $request): Nette\Application\Response
    {
        $exception = $request->getParameter('exception');

        if ($exception instanceof Nette\Application\BadRequestException) {
            $urlScript = $this->httpRequest->getUrl();
            $subpath = substr($urlScript->path, strlen($urlScript->basePath));  // tady máš url
            $module = str_starts_with($subpath, 'admin/') ? 'Admin' : 'Front';
            $sep = ':';
            return new Responses\ForwardResponse($request->setPresenterName($module . $sep . 'Error4xx'));
        }

        $this->logger->log($exception, ILogger::EXCEPTION);
        return new Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse): void {
            if (preg_match('#^text/html(?:;|$)#', (string) $httpResponse->getHeader('Content-Type'))) {
                require __DIR__ . '/../templates/Error/500.phtml';
            }
        });
    }
}

Budeš to muset ještě doladit, ale Tvoje trable by to mohlo snadno vyřešit.

Editoval m.brecher (6. 4. 2023 1:54)

d@rkWolf
Člen | 167
+
0
-

@m.brecher

jo, na to mělo být právě použité toto:

            // forward only to admin or front error4xx presenter
            if (!strstr($module, 'Admin')) {
                $module = ($sep === ':') ? 'Front' : 'Front:';
            }

Já jsem to nějak zkoušel a nějak mi z toho vylezlo toto, ale nejspíš mě tehdy nenapadly ani zdaleka všechny varianty toho, co mi tam může přijít… Asi mi vycházelo, že když to identifikuje modul Admin, bude v $module proměnné Admin pokud tam bude cokoliv jiného nebo nic, dá se tam Front.

Vyzkouším to s tím rozložením url a uvidím, jak to půjde.

Škoda, že řešení rozdělení Error presenterů není v Nette examplech, teď už dokonce je v User-authentication examplu rozdělení do modulů, ale Errory jsou zase jen v jednom.

m.brecher
Generous Backer | 864
+
0
-

d@rkWolf napsal(a):

jo, na to mělo být právě použité toto:

            // forward only to admin or front error4xx presenter
            if (!strstr($module, 'Admin')) {
                $module = ($sep === ':') ? 'Front' : 'Front:';
            }

Já jsem to nějak zkoušel a nějak mi z toho vylezlo toto, ale nejspíš mě tehdy nenapadly ani zdaleka všechny varianty toho, co mi tam může přijít… Asi mi vycházelo, že když to identifikuje modul Admin, bude v $module proměnné Admin pokud tam bude cokoliv jiného nebo nic, dá se tam Front.

Já jsem se trápil s ErrorPresenterem už docela dávno, a jestli si to ještě správně pamatuji, tak:

a) pokud BadRequestException vyhodí Presenter::error() tak informaci o modulu v Requestu máš,

b) pokud BadRequestException vyhodí už Router tak v Requestu informaci o aktuálním modulu nemáš, jsou to ty 404ky, kde Tracy hlásí „No route for HTTP request“.

Proto je lepší nespoléhat na modul v requestu a raději si v ErrorPresenteru znovu zanalyzovat url, což není nic těžkého.

Editoval m.brecher (17. 4. 2023 14:49)

Marek Bartoš
Nette Blogger | 1264
+
0
-

Jestli tě nezajímají routy, co nematchnuly vůbec, tak url vůbec analyzovat nemusíš :)
https://github.com/…resenter.php

m.brecher
Generous Backer | 864
+
0
-

@MarekBartoš

Jestli tě nezajímají routy, co nematchnuly vůbec, tak url vůbec analyzovat nemusíš :)
https://github.com/…resenter.php

Moc pěkný. Jestli jsem to správně pochopil tak:

a) si někde namapuješ skupiny presenterů (ideálně celé moduly) na errorPresentery (::addErrorPresenter())

b) výjimku přehodíš na DefaultErrorPresenter

c) DefaultErrorPresenter matchne request a forwardne výjimku na modulový/defaultní errorPresenter

d) modulový errorPresenter si 403, 404, 500 vyřeší každý po svém, lze tedy i stránky 500 vykreslit do odpovídajícího layoutu

To se mě líbí, to budu používat :)

Marek Bartoš
Nette Blogger | 1264
+
+1
-

@m.brecher Mrkni na celou složku Error, je to tam všechno pohromadě. Až na to, že tam neexistuje mapování presenterů a odkazuju se na třídu a na to, že jsou latte soubory ve složce s presenterem by to mělo být pochopitelné.

Zajímají tě UI\ErrorForwardPresenter, UI\ErrorPresenterUtil, Admin\ErrorPresenter a wiring.neon

A ano, chápeš to správně

Editoval Marek Bartoš (17. 4. 2023 15:35)