6

I have a module that in hook_preprocess() checks with drupal_get_http_header() whether Drupal is returning a 403 or 404 error, but in Drupal 8 that function is deprecated, since header handling is being shifted to a Symfony response object.

The used code is similar to the following one.

$header = drupal_get_http_header('Status');

switch ($header) {
  case '403 Forbidden':
    // Add new headers.
    // …
    break;

  case '404 Not Found':
    // Add new headers.
    // …
    break;
}

What code should I use in Drupal 8? If that means to use a different hook, that is fine.

I would also be interested in the case I need to add HTML output to the page. I guess that in this case I should use hook_page_alter(), but I still don't know how to access the response object for the page being served.

1
4

You can subscribe to the Symfony KernelEvents::RESPONSE event:

The RESPONSE event occurs once a response was created for replying to a request

This event allows you to modify or replace the response that will be replied. The event listener method receives a Symfony\Component\HttpKernel\Event\FilterResponseEvent instance.

To do so, create a new module ("response_test" in this example), with the usual .info.yml and .module file.

Then register your event subscriber in the module's services file.

response_test.services.yml

services:
    response_test.response_subscriber:
      class: Drupal\response_test\ResponseSubscriber
      tags:
        - { name: event_subscriber }

Now all you need is the class to handle the event.

lib/Drupal/response_test/ResponseSubscriber.php (src/ResponseSubscriber.php in latest Drupal 8 versions)

namespace Drupal\response_test;

use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ResponseSubscriber implements EventSubscriberInterface {
  
  public function onRespond(FilterResponseEvent $event) {
    if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
      return;
    }
    
    $response = $event->getResponse();
    if ($response->getStatusCode() == 404) {
      // Prepare a new Symfony\Component\HttpFoundation\Response and use
      $event->setResponse($new_response);

      // or call one of the setter methods on $response, the changes will persist
    }
  }
  
  public static function getSubscribedEvents() {
    $events[KernelEvents::RESPONSE][] = array('onRespond');
    return $events;
  }
  
}

I'm a little unsure about changing the HTML content in this context; you have access to $response->getContent() and $response->setContent() so you can manipulate the raw string directly, but as far as I can tell everything's already rendered at this point.

3
  • Should lib/Drupal/response_test/ResponseSubscriber.php be src/EventSubscriber/ResponseSubscriber.php ? – Chris Happy Jun 21 at 22:08
  • Curious, because I referenced your answer here: drupal.stackexchange.com/a/303690 (Great answer, BTW) – Chris Happy Jun 21 at 22:09
  • 1
    @ChrisHappy On earlier versions, Drupal didn't follow PSR-4; that's why the class file is said to be in that directory. Event subscriber classes aren't required to be in the EventSubscriber directory, although that is the directory usually used from Drupal core modules. – apaderno Jun 22 at 7:19
3

To check for error codes you can use the following snippet. It won't work for 200 status codes though. Since there's no exception.

use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

$exception = \Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
if ($exception instanceof HttpExceptionInterface) {
  $status_code = $exception->getStatusCode();
  // ...
}

I'm unsure if it's safe to assume that the status code must be 200 if there's no exception in the first place. If yes you could use the following snippet to cover a 200er response.

if (!\Drupal::requestStack()->getCurrentRequest()->attributes->has('exception')) {
  $status_code = '200';
}
8
  • The request attribute exception is only present in a subrequest, which Drupal uses to build a themed 403/404 response with configurable content. The standard exception handling, which takes place in the main request, does not use this attribute. The status code is accessible in the exception thrown and once handled by KernelEvents::EXCEPTION in the response. Which don't necessarily contain the same status code, the exception subscriber can put in the response what it wants. – 4k4 Nov 16 '19 at 11:08
  • See for example drupal.stackexchange.com/questions/231195/… – 4k4 Nov 16 '19 at 11:15
  • @4k4 – So using this snippet if there's an exception the status code returned always only can be 403 or 404, right? So the assumption that if no exception is returned the status code must be 200 is not entirely safe, right? And to always get the current request's status code I'd need an event subscriber? That I can create a service for and inject it where I want to get the status code then? – leymannx Nov 17 '19 at 11:57
  • The only thing you can tell by the absence of this attribute in the request is that you are not in this special kind of subrequest. It's hard to explain this is one comment. Drupal uses this subrequest to start the page building again, after the page building of the main request was interrupted by an exception. Now for a different path of the content the user has configured to be the error page, and when the subrequest is finished, it puts the rendered content in the response of the main request. – 4k4 Nov 17 '19 at 14:55
  • 1
    Link /admin/config/system/site-information and explain the visibility is for these configurable error pages and the options in this context would then be "403 page" "404 page" and "no error page" – 4k4 Nov 17 '19 at 16:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.