/** * Create a Failed API Response from an Exception * * The HttpKernel exception catcher tries to enforce an error HTTP response code. * This can be avoided by using the X-Status-Code header. * * @param GetResponseForExceptionEvent $event */ public function onKernelException(GetResponseForExceptionEvent $event) { $request = $event->getRequest(); $pathConfig = $this->configCompiler->compileApiConfig($request); if (null === $pathConfig) { // This is not an API endpoint. return; } $exception = $event->getException(); // Check if this is an OPTIONS request. if ($exception instanceof MethodNotAllowedHttpException && $request->getMethod() === Request::METHOD_OPTIONS) { // A MethodNotAllowedHttpException implies that the route exists but not for the requested HTTP method. // In the case of a an OPTIONS request (CORS preflight) we send a 200 OK instead of a 404 Not Found. $response = $this->responseGenerator->generateSuccessResponse(); // Explicitly set the 200 OK status code as the X-Status-Code header so the HttpKernel allows the 200. /** @see Symfony\Component\HttpKernel\HttpKernel::handleException() */ $response->headers->set('X-Status-Code', Response::HTTP_OK); // The MethodNotAllowedHttpException exception is populated with the methods that do exist for a route. // Use these existing methods in the Allow header. $exceptionHeaders = $exception->getHeaders(); $response->headers->set('Allow', isset($exceptionHeaders['Allow']) ? $exceptionHeaders['Allow'] : null); $event->setResponse($response); return; } $httpCode = null; $errorCode = null; $errorTitle = null; $errorData = null; // Determine the API error code, API error title, and HTTP status code // depending on the type of exception thrown. if ($exception instanceof ApiResponseExceptionInterface) { // There is a separate HTTP status code that can also be set on the exception. The default is 400. $httpCode = $exception->getHttpStatusCode(); $errorCode = $exception->getCode(); $errorTitle = $exception->getMessage(); $errorData = $exception->getErrorData(); } elseif ($exception instanceof HttpExceptionInterface) { // Use the code from the Symfony HTTP exception as both the API error code and the HTTP status code. $httpCode = $exception->getStatusCode(); $errorCode = $exception->getStatusCode(); $errorTitle = Response::$statusTexts[$exception->getStatusCode()]; } elseif ($exception instanceof AuthenticationException) { // Authentication exceptions use 401 for both the API error code and the HTTP status code. $httpCode = Response::HTTP_UNAUTHORIZED; $errorCode = Response::HTTP_UNAUTHORIZED; $errorTitle = Response::$statusTexts[Response::HTTP_UNAUTHORIZED]; } elseif ($exception instanceof AccessDeniedException) { // Authorization exceptions use 403 for both the API error code and the HTTP status code. $httpCode = Response::HTTP_FORBIDDEN; $errorCode = Response::HTTP_FORBIDDEN; $errorTitle = Response::$statusTexts[Response::HTTP_FORBIDDEN]; } else { // All other errors use 500 for both the API error code and the HTTP status code. $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; $errorCode = Response::HTTP_INTERNAL_SERVER_ERROR; // The API error title is determined based on the environment. if ($this->debug) { // For debug environments, exception messages and trace get passed straight through to the client. $message = sprintf('exception \'%s\' with message \'%s\' in %s:%s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()); $errorTitle = $message; $errorData = $exception->getTraceAsString(); } else { // For non-debug environments, use the corresponding generic HTTP status message as the API error title. $errorTitle = Response::$statusTexts[Response::HTTP_INTERNAL_SERVER_ERROR]; } } $response = $this->responseGenerator->generateErrorResponse($httpCode, $errorCode, $errorTitle, $errorData); $event->setResponse($response); }
public function testSuccessResponseWithSpecificSerializer() { $factoryMock = \Mockery::mock(SerializerAdapterFactory::class)->shouldReceive('createSerializerAdapter')->once()->with('other_serializer')->andReturn(new JsonEncodeSerializerAdapter())->getMock(); $generator = new ApiResponseGenerator($factoryMock); $generator->generateSuccessResponse('foobar', null, [], 'other_serializer'); }