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

The question:

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.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

First of all, to comply with action controller interface MagentoFrameworkAppActionInterface::execute(), your action must return an instance of MagentoFrameworkControllerResultInterface (MagentoFrameworkAppResponseInterface 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 MagentoFrameworkControllerResultInterface. The most suitable for custom REST API (assuming it operates with JSON) seems to be MagentoFrameworkControllerResultJson. However, if you need something even more custom, consider MagentoFrameworkControllerResultRaw.

Working sample:

<?php
namespace VendorNameModuleNameController;

/**
 * Demo of authorization error for custom REST API
 */
class RestAuthorizationDemo extends MagentoFrameworkAppActionAction
{
    /** @var MagentoFrameworkControllerResultJsonFactory */
    protected $jsonResultFactory;

    public function __construct(
        MagentoFrameworkAppActionContext $context,
        MagentoFrameworkControllerResultJsonFactory $jsonResultFactory
    ) {
        parent::__construct($context);
        $this->jsonResultFactory = $jsonResultFactory;
    }

    public function execute()
    {
        /** @var MagentoFrameworkControllerResultJson $result */
        $result = $this->jsonResultFactory->create();
        /** You may introduce your own constants for this custom REST API */
        $result->setHttpResponseCode(MagentoFrameworkWebapiException::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?"}

Method 2

Magento 2 provides a MagentoFrameworkAppResponseInterface abstract type for you to instantiate via the dependency injection. Object manager used below for simplicity’s sake.

/** @var MagentoFrameworkAppObjectManager $om */
$om = MagentoFrameworkAppObjectManager::getInstance();
/** @var     MagentoFrameworkAppResponseInterface|MagentoFrameworkAppResponseHttp $response */
$response = $om->get('MagentoFrameworkAppResponseInterface');
$response->setHeader('<header name>', '<header value>', $overwriteExisting = true);

For example

$response->setHeader('Content-Transfer-Encoding', 'binary', true);

Status headers are considered a special case — you’ll want to use the setStatusCode message here. Also, controller’s have access to a getResponse message, so you can avoid the messy business of injecting on a controller

public function execute()
{      
    $this->getResponse()
        ->setStatusCode(MagentoFrameworkAppResponseHttp::STATUS_CODE_403)
        ->setContent('Error');
    return ;
}

Method 3

In Magento 2.1 and above, easiest way is to use the result factory:

public function execute()
{
    $resultPage = $this->resultFactory
        ->create(ResultFactory::TYPE_JSON)
        ->setHeader("myheaderkey", "value")
        ->setHttpResponseCode(403);

    return $resultPage;
}

The ResultFactory is already injected into your controller because of inheritance of MagentoFrameworkAppActionAction.

Moreover, you’re programming against MagentoFrameworkControllerResultInterface, which is the expected (or recommended) return type of a controller.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment