404 page not shown if catalog not exist

Questions around the TYPO3 integration and plugins
Forum rules
Always add your TYPO3, Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
heural
Posts: 88
Joined: 09 Jun 2022, 07:55

404 page not shown if catalog not exist

Post by heural » 05 Sep 2025, 12:01

Hi Aimeos-Team,

if we call a catalog-URL that does not exist, instead of showing 404 page, the exception is thrown (catalog with id 123 does not exist)
For product (detail-page) the 404 page is shown.

catalog id 123 not exists:

/shop/123/category-not-exist : exception, should 404
/detail/123/category-not-exist/product-name: exception, should 404
/detail/1234/category-exist/product-name-not-exist: 404, its ok

The TYPO3 error handling is "Show content from pid".

The route enhancer block from siteconfig:

Code: Select all

routeEnhancers:
  Aimeos:
    type: Extbase
    namespace: ai
    defaultController: 'Catalog::list'
    # only on aimeos pages 
    limitToPages:
      - 1323
      - 1326
      - 1331
      - 1328
      - 1365
      - 2022
      - 2025
      - 1344
      - 1340
      - 1343
      - 1337
    routes:
      -
        routePath: '/pin/{pin_action}/{pin_id}/{d_name}'
        _controller: 'Catalog::detail'
      -
        routePath: '/history/{his_action}/{his_id}'
        _controller: 'Account::history'
      -
        routePath: '/watch/{wat_action}/{wat_id}'
        _controller: 'Account::watch'
      -
        routePath: '/watch/{wat_action}'
        _controller: 'Account::watch'
      -
        routePath: '/fav/{fav_action}/{fav_id}'
        _controller: 'Account::favorite'
      -
        routePath: '/fav/{fav_action}'
        _controller: 'Account::favorite'
      -
        routePath: '/b/{b_action}'
        _controller: 'Basket::index'
      -
        routePath: '/co/{c_step}'
        _controller: 'Checkout::index'
      -
        routePath: '/p/{d_name}/{d_prodid}/{d_pos}'
        _controller: 'Catalog::detail'
      -
        routePath: '/lt/{l_type}'
        _controller: 'Catalog::list'
      -
        routePath: '/lp/{l_page}'
        _controller: 'Catalog::list'
      -
        routePath: '/ls/{f_sort}/{l_page}'
        _controller: 'Catalog::list'
      -
        routePath: '/l/{f_sort}'
        _controller: 'Catalog::list'
      -
        routePath: '/{f_catid}/{f_name}'
        _controller: 'Catalog::list'
      -
        routePath: '/{f_catid}/{f_name}/{d_name}'
        _controller: 'Catalog::detail'
    requirements:
      pin_id: \d+
      his_id: \d+
      wat_id: \d+
      fav_id: \d+
      d_prodid: \d+
      d_pos: '\d*'
      l_page: \d+
      f_catid: \d+
      d_name: '[^/]+'
      f_name: '[^/]+'
      f_sort: '[A-Za-z0-9_-]*'
      l_type: '[A-Za-z0-9_-]+'
      b_action: '[A-Za-z0-9_-]*'
      c_step: '[A-Za-z0-9_-]*'
    defaults:
      b_action: ''
      c_step: ''
      f_name: c
      f_sort: ''
      d_pos: '
      
We have
- Multi-Site
- Aimeos 2024
- TYPO3 v12.4.36

And is it possible to can do the routing without the catalog id? The detail/product url works fine without the product id.

Thank you for your support!
Thanks a lot!

boettner
Advanced
Posts: 137
Joined: 09 Feb 2015, 17:49

Re: 404 page not shown if catalog not exist

Post by boettner » 05 Sep 2025, 12:11

Hi,

I am using a Middleware and a custom page not found Handler for such cases:

Middleware:

Code: Select all

<?php

namespace Your\Ext\Middleware;

use Your\Ext\Error\PageNotFoundHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class NotFoundToErrorHandlerMiddleware implements MiddlewareInterface
{
    /**
     * @throws Throwable
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        try {
            $response = $handler->handle($request);
        } catch (Throwable $e) {
            // Convert application exceptions (e.g., Aimeos errors) into your custom 404 page
            try {
                /** @var PageNotFoundHandler $handlerService */
                $handlerService = GeneralUtility::makeInstance(PageNotFoundHandler::class);
                return $handlerService->handlePageError($request, 'Application threw exception: ' . $e->getMessage());
            } catch (Throwable) {
                // As a last resort, rethrow to keep default TYPO3 exception handling
                throw $e;
            }
        }

        // Probe header to verify middleware runs on successful requests too
        $response = $response->withHeader('X-NotFound-Middleware', 'on');

        // Turn plugin-level 404 errors into your custom 404 page
        if ($response->getStatusCode() === 404 && $this->isHtml($response)) {
            try {
                /** @var PageNotFoundHandler $handlerService */
                $handlerService = GeneralUtility::makeInstance(PageNotFoundHandler::class);
                return $handlerService->handlePageError($request, 'Application returned 404');
            } catch (Throwable) {
                return $response;
            }
        }

        return $response;
    }

    private function isHtml(ResponseInterface $response): bool
    {
        $type = $response->getHeaderLine('Content-Type');
        return $type === '' || str_starts_with($type, 'text/html');
    }
}
Middleware registration:

Code: Select all

return [
    'frontend' => [
        'your/ext/notfound-to-error' => [
            'target' => NotFoundToErrorHandlerMiddleware::class,
            'after' => [
                'typo3/cms-frontend/tsfe',
            ],
            'before' => [
                'typo3/cms-frontend/content-length-headers',
                'typo3/cms-frontend/response-propagation',
            ],
        ],
];
Handler:

Code: Select all

<?php

namespace Your\Ext\Error;

use LogicException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class PageNotFoundHandler implements PageErrorHandlerInterface
{
    /**
     * @var int
     */
    protected int $pageNotFoundUid = 1;

    /**
     * @var ServerRequestInterface
     */
    protected ?ServerRequestInterface $request = null;


    public function handlePageError(
        ServerRequestInterface $request,
        string                 $message,
        array                  $reasons = []
    ): ResponseInterface
    {
        $this->request = $request;
        return new HtmlResponse($this->getPageNotFoundContent(), 404);
    }

    /**
     * @return string
     */
    protected function getPageNotFoundContent(): string
    {
        $url = $this->getPageNotFoundUrl();
        $requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
        /** @var ResponseInterface $response */
        $response = $requestFactory->request($url, 'GET');
        if (($response->getStatusCode() === 200) &&
            str_starts_with($response->getHeaderLine('Content-Type'), 'text/html')) {
            return $response->getBody()->getContents();
        }
        throw new LogicException('Could not read content of ' . $url, 1594134183);
    }

    /**
     * @return string
     */
    protected function getPageNotFoundUrl(): string
    {
        /** @var Site $site */
        $site = $this->request->getAttribute('site');
        /** @var Uri $uri */
        $uri = $site->getRouter()->generateUri($this->pageNotFoundUid, ['_language' => $this->getLanguageIdentifier()]);
        return $uri->__toString();
    }

    /**
     * @return int
     */
    protected function getLanguageIdentifier(): int
    {
        /** @var SiteLanguage $language */
        $language = $this->request->getAttribute('language');
        return $language->getLanguageId();
    }
}
Here are the docs for Middlewares and custom error handlers in case you don't know how to configure it:
https://docs.typo3.org/m/typo3/referenc ... wares.html
https://docs.typo3.org/m/typo3/referenc ... Index.html

Post Reply