Search code examples
phpjsonzend-framework2

Remove additional messages from 404 JSON response in Zend Framework 2


I'm creating a RESTful API in ZF2 returning JSON responses for each route. For one of the routes, I need to return a 404 response, but return $this->notFoundAction(); is not acceptable as it returns a view rather than JSON. I'm therefore doing the following:

$response = $this->getResponse();
$response->setStatusCode(404);
return new JsonModel(array(
    'error_message' => "Client $clientRef doesn't exist"
));

Testing this, I get the response fine with a 404 status code, and my error message is displayed. However, there are other Zend-generated messages included as part of the response:

{
    "error_message": "Client test-client doesn't exist",
    "message": "Page not found.",
    "reason": "error-controller-cannot-dispatch",
    "display_exceptions": true,
    "controller": "Timesheet\\Controller\\TimesheetRest",
    "controller_class": null
}

If I change display_not_found_reason and display_exceptions both to false in the config, then it will remove some of the additional details, but message still remains:

{
    "error_message": "Client test-client doesn't exist",
    "message": "Page not found."
}

How do I remove all the extra details from the response body?

Note that I only get these messages when returning a 404 status. When returning 400, 403, or 409, it is only my error message that is returned, without any additional messages.

Not sure if it's relevant, but the config file for the module in question is as follows (note I have 2 controllers, one for the front-end, and one for the API):

<?php
return array(
    'controllers' => array(
        'invokables' => array(
            'Timesheet\Controller\Timesheet' => 'Timesheet\Controller\TimesheetController',
            'Timesheet\Controller\TimesheetRest' => 'Timesheet\Controller\TimesheetRestController'
        )
    ),
    'router' => array(
        'routes' => array(
            'timesheet' => array(
                'type' => 'Segment',
                'options' => array(
                    // Change this to something specific to your module
                    'route' => '/timesheet[/:action][/:id]',
                    'defaults' => array(
                        // Change this value to reflect the namespace in which
                        // the controllers for your module are found
                        '__NAMESPACE__' => 'Timesheet\Controller',
                        'controller' => 'Timesheet',
                        'action' => 'index'
                    ),
                    'constraints' => array(
                        'action' => 'index|pending|review|save|submit|unauthorised',
                        'id' => '[1-9][0-9]*'
                    )
                )
            ),
            'timesheet-rest' => array(
                'type' => 'Segment',
                'options' => array(
                    // Change this to something specific to your module
                    'route' => '/api/timesheet[/:id]',
                    'defaults' => array(
                        // Change this value to reflect the namespace in which
                        // the controllers for your module are found
                        '__NAMESPACE__' => 'Timesheet\Controller',
                        'controller' => 'TimesheetRest',
                    ),
                    'constraints' => array(
                        'id' => '[1-9][0-9]*'
                    )
                )
            ),
        )
    ),
    'view_manager' => array(
        'strategies' => array(
            'ViewJsonStrategy'
        ),
        'template_path_stack' => array(
            'Timesheet' => __DIR__ . '/../view'
        )
    )
);

Solution

  • Show blank message

    After looking through Zend/Mvc/View/Http/RouteNotFoundStrategy there is a function prepareNotFoundViewModel which is what adds message to the response. However, this only happens when message isn't already defined. Changing my code to this:

    $response = $this->getResponse();
    $response->setStatusCode(404);
    return new JsonModel(array(
        'message' => '',
        'error_message' => "Client $clientRef doesn't exist"
    ));
    

    would then give me a response body of:

    {
        "message": "",
        "error_message": "Client test-client doesn't exist"
    }
    

    which at least removes confusion between the two messages.

    Remove message

    However, changing the code to return the response directly, instead of using a JsonModel, stops message from being injected at all. The following code:

    $response = $this->getResponse();
    $response->setStatusCode(404);
    $response->setContent(json_encode(array(
        'error_message' => "Client $clientRef doesn't exist"
    )));
    return $response;
    

    gives this as a response:

    {
        "error_message":"Client test-client doesn't exist"
    }