Set default renderer to JsonRenderer using laminas-view

Hey folks

Hope your fine! I’m looking for a way to change the default renderer from PhpRenderer to JsonRenderer on a mezzio application. How can I do that? I want to output JSON by default.

I’ve tried to change the dependencies.global.php to:

return [
'dependencies' => [
    'factories' => [
        \Laminas\View\Renderer\PhpRenderer::class => \Laminas\View\Renderer\JsonRenderer::class
    ],
],];

Thanks in advance
Roman

Hello and welcome to our forums! :smiley:

For an output in JSON you do not need laminas-view. You can use Laminas\Diactoros\Response\JsonResponse. Example:

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Laminas\Diactoros\Response\JsonResponse;

// …

public function handle(ServerRequestInterface $request) : ResponseInterface
{
    // …

    return new JsonResponse($dataToConvertToJson);
}

Can you describe your use case and why the renderer is needed?

Thanks a lot! :grinning:

Beside our custom handlers, which already returns a JsonResponse, I’d like to output every exception the application is throwing as a JSON to the user. Currently the NotFoundHandler uses LaminasViewRender -> PhpRender if a template was found or it returns a TextResponse.

I was looking at the NotFoundHandlerFactory and tried to override the TemplateRendererInterface::class in dependencies.global.php:

return [
'dependencies' => [
    'factories' => [
        TemplateRendererInterface::class => \Laminas\View\Renderer\JsonRenderer::class
    ],
],];

My goal is to always respond in JSON if no specific renderer/template was set. I’ve already tried to use mezzio/mezzio-problem-details for this purpose. But a custom exception is now rendered as a HTTP 500 error without the exception message body. Isn’t there a simpler way of doing this without additional dependecies?

Thanks a lot
Roman

You could just create your own ExceptionMiddleware

class ExceptionMiddleware implements MiddlewareInterface
{
    /** @var ProblemDetailsResponseFactory */
    private $responseFactory;

    /**
     * Initialize new instance of ExceptionMiddleware
     *
     * @param ProblemDetailsResponseFactory $responseFactory
     */
    public function __construct(ProblemDetailsResponseFactory $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    /**
     * Catch all exceptions and return a proper json response
     *
     * @param ServerRequestInterface  $request
     * @param RequestHandlerInterface $handler
     *
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        try {
            return $handler->handle($request);
        } catch (Throwable $throwable) {
            return $this->responseFactory->createResponse(
                $request,
                $this->normalizeHttpCode((int) $throwable->getCode()),
                $throwable->getMessage()
            );
        }
    }

    /**
     * Return 500 if error code is out of range
     *
     * @param int $code
     *
     * @return int
     */
    private function normalizeHttpCode(int $code): int
    {
        if ($code < 100 || $code > 599) {
            $code = 500;
        }

        return $code;
    }
}

Did you set it up correctly? https://docs.mezzio.dev/mezzio-problem-details/quick-start/#error-handling

You need to pipe the ProblemDetailsMiddleware and optionaly use the NotFoundHandler.

Thanks a lot for your help guys! I’ve tried the custom ErrorMiddleware approach but ended up using mezzio/mezzio-problem-details with a custom factory to include the custom error messages in the output regardless of the debug mode.

class CustomProblemDetailsResponseFactoryFactory
{
    public function __invoke(ContainerInterface $container) : ProblemDetailsResponseFactory
    {
        $config = $container->has('config') ? $container->get('config') : [];
        $problemDetailsConfig = $config['problem-details'] ?? [];

        $isDebug = $config['debug'] ?? false;
        $includeThrowableDetail = $isDebug ?? ProblemDetailsResponseFactory::EXCLUDE_THROWABLE_DETAILS;
        $includeThrowableDetail = $problemDetailsConfig['include_exception_details'] ?? $includeThrowableDetail;
        $jsonFlags = $problemDetailsConfig['json_flags'] ?? null;
        $defaultTypesMap = $problemDetailsConfig['default_types_map'] ?? [];

        return new ProblemDetailsResponseFactory(
            $container->get(ResponseInterface::class),
            $isDebug,
            $jsonFlags,
            $includeThrowableDetail,
            \Mezzio\ProblemDetails\ProblemDetailsResponseFactory::DEFAULT_DETAIL_MESSAGE,
            $defaultTypesMap
        );
    }
}