Skip to content

Instantly share code, notes, and snippets.

@bwaidelich
Created June 19, 2019 09:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bwaidelich/485172fb7c01c7aedf4e93b43875d98f to your computer and use it in GitHub Desktop.
Save bwaidelich/485172fb7c01c7aedf4e93b43875d98f to your computer and use it in GitHub Desktop.
Render Neos / Flow exceptions as JSON if a corresponding Accept / Content-Type header is set
<?php
declare(strict_types=1);
namespace Some\Package\Error;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Helper\ResponseInformationHelper;
use Neos\Flow\Http\Request;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\View\ViewInterface;
use Neos\Utility\MediaTypes;
/**
* A View that can be used as exception view and that renders exceptions as JSON if the current HTTP request
* sends a corresponding "Accept" or "Content-Type" header.
*
* Example configuration:
*
* Flow:
* error:
* exceptionHandler:
* defaultRenderingOptions:
* viewClassName: 'Some\Package\Error\JsonCapableExceptionView'
* templatePathAndFilename: 'this-is-required'
*
* @Flow\Proxy(false)
*/
final class JsonCapableExceptionView implements ViewInterface
{
/**
* @var ControllerContext
*/
private $controllerContext;
/**
* @var array
*/
private $variables = [];
public static function createWithOptions(array $options): self
{
return new static();
}
public function setControllerContext(ControllerContext $controllerContext): void
{
$this->controllerContext = $controllerContext;
}
public function assign($key, $value): self
{
$this->variables[$key] = $value;
return $this;
}
public function assignMultiple(array $values): self
{
foreach ($values as $key => $value) {
$this->assign($key, $value);
}
return $this;
}
public function canRender(ControllerContext $controllerContext): bool
{
return true;
}
public function render(): string
{
$statusCode = $this->variables['statusCode'] ?? 500;
$referenceCode = $this->variables['referenceCode'] ?? null;
$statusMessage = ResponseInformationHelper::getStatusMessageByCode($statusCode);
$referenceCodeMessage = ($referenceCode !== null) ? '<p>When contacting the maintainer of this application please mention the following reference code:<br /><br />' . $referenceCode . '</p>' : '';
if ($this->isJsonRequest()) {
header('Content-Type: application/json');
$error = [
'message' => $statusMessage,
'code' => $statusCode,
'referenceCode' => $referenceCode,
];
return json_encode($error);
}
return '<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>' . $statusCode . ' ' . $statusMessage . '</title>
<style type="text/css">
body {
font-family: Helvetica, Arial, sans-serif;
margin: 50px;
}
h1 {
color: #00ADEE;
font-weight: normal;
}
</style>
</head>
<body>
<h1>' . $statusCode . ' ' . $statusMessage . '</h1>
<p>An internal error occurred.</p>
' . $referenceCodeMessage . '
</body>
</html>';
}
private function isJsonRequest(): bool
{
$request = Request::createFromEnvironment();
$mediaTypeHeader = $request->hasHeader('Accept') && $request->getHeader('Accept') !== '*/*' ? $request->getHeader('Accept') : $request->getHeader('Content-Type');
if ($mediaTypeHeader === null) {
return false;
}
$mediaType = MediaTypes::parseMediaType($mediaTypeHeader);
return $mediaType['subtype'] === 'json';
}
}
Neos:
Flow:
error:
exceptionHandler:
defaultRenderingOptions:
viewClassName: 'Some\Package\Error\JsonCapableExceptionView'
templatePathAndFilename: 'this-is-required'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment