Magento 2: Sending a Custom Header/Response from a Controller

http-requestmagento2PHPresponse

In Magento 2 is it possible, from a controller execute method, to manipulate the request to send a custom header and error page.

In Context: We're implementing a small, limited REST API endpoint that returns specific data. We don't want to use the new Magento 2 API endpoints because 1. Unproven; 2. User experience in setting up authentication. 3. Full control over data formats to support the specific application. Suggestions to do so will be smiled at, and politely ignored.

I'd like to send a proper 403 Forbidden response when the API receives unauthorized requests. I realize I could do something like

function execute()
{
    if(not authorized)
    {
        header('HTTP/1.0 403 Forbidden');
        echo "What are you doing here?";
        exit;
    }
}

But that's less than ideal — especially since halting execution will stop other events and Magento processes from firing.

Most PHP frameworks have a way to directly Manipulate the Response to set a custom header and response string.

Does Magento 2 have something similar?

If so, what's the syntax/procedure/best-practice for using it in a controller's execute method? Can I just get a reference to the response object and set things? Do I need to return something specific from execute (instead of the standard page factory). Is there a higher level abstraction for doing this that doesn't require manipulating the response? Throwing a specific exception? etc.

Best Answer

First of all, to comply with action controller interface \Magento\Framework\App\ActionInterface::execute(), your action must return an instance of \Magento\Framework\Controller\ResultInterface (\Magento\Framework\App\ResponseInterface is also supported, but is legacy and will be removed in future releases of M2, when all core usages are refactored).

So choose from available implementations of \Magento\Framework\Controller\ResultInterface. The most suitable for custom REST API (assuming it operates with JSON) seems to be \Magento\Framework\Controller\Result\Json. However, if you need something even more custom, consider \Magento\Framework\Controller\Result\Raw.

Working sample:

<?php
namespace VendorName\ModuleName\Controller;

/**
 * Demo of authorization error for custom REST API
 */
class RestAuthorizationDemo extends \Magento\Framework\App\Action\Action
{
    /** @var \Magento\Framework\Controller\Result\JsonFactory */
    protected $jsonResultFactory;

    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\Controller\Result\JsonFactory $jsonResultFactory
    ) {
        parent::__construct($context);
        $this->jsonResultFactory = $jsonResultFactory;
    }

    public function execute()
    {
        /** @var \Magento\Framework\Controller\Result\Json $result */
        $result = $this->jsonResultFactory->create();
        /** You may introduce your own constants for this custom REST API */
        $result->setHttpResponseCode(\Magento\Framework\Webapi\Exception::HTTP_FORBIDDEN);
        $result->setData(['error_message' => __('What are you doing here?')]);
        return $result;
    }
}

The code above will result in response with HTTP status code 403 and body {"error_message":"What are you doing here?"}