forked from loafle/openapi-generator-original
recent commit 40894382fc already improved
that method: before that other commit it was juste impossible to query
a endpoint with a response type that was something else than
application/json or application/xml. With that commit it became possible
to query such endpoint provided that the client declare in its Accept
header that it can cope with */* (or provided that the client omitted
that header altogether).
But there were still cases badly handled. For instance if an endpoint
returns a response of type image/png and that it receives a query with
header "Accept: image/png", then it would reply with 406.
To avoid any other issue with type resolution, this commit revamps the
getOutputFormat function more thoroughly and does it by implementing
the specification available at
https://httpwg.org/specs/rfc9110.html#field.accept ), which means that
the format accepted by the client are ordered by the relative weights
specified it specified.
283 lines
8.8 KiB
PHP
283 lines
8.8 KiB
PHP
<?php
|
|
/**
|
|
* Controller
|
|
*
|
|
* PHP version 8.1.1
|
|
*
|
|
* @category Class
|
|
* @package OpenAPI\Server\Controller
|
|
* @author OpenAPI Generator team
|
|
* @link https://github.com/openapitools/openapi-generator
|
|
*/
|
|
|
|
/**
|
|
* OpenAPI Petstore
|
|
*
|
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
|
*
|
|
* The version of the OpenAPI document: 1.0.0
|
|
*
|
|
* Generated by: https://github.com/openapitools/openapi-generator.git
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* NOTE: This class is auto generated by the openapi generator program.
|
|
* https://github.com/openapitools/openapi-generator
|
|
* Do not edit the class manually.
|
|
*/
|
|
|
|
namespace OpenAPI\Server\Controller;
|
|
|
|
use OpenAPI\Server\Api\ApiServer;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
use Symfony\Component\Validator\ConstraintViolation;
|
|
use OpenAPI\Server\Service\SerializerInterface;
|
|
use OpenAPI\Server\Service\ValidatorInterface;
|
|
|
|
/**
|
|
* Controller Class Doc Comment
|
|
*
|
|
* @category Class
|
|
* @package OpenAPI\Server\Controller
|
|
* @author OpenAPI Generator team
|
|
* @link https://github.com/openapitools/openapi-generator
|
|
*/
|
|
class Controller extends AbstractController
|
|
{
|
|
protected ValidatorInterface $validator;
|
|
protected SerializerInterface $serializer;
|
|
protected ApiServer $apiServer;
|
|
|
|
public function setValidator(ValidatorInterface $validator): self
|
|
{
|
|
$this->validator = $validator;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setSerializer(SerializerInterface $serializer): self
|
|
{
|
|
$this->serializer = $serializer;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setApiServer(ApiServer $server): self
|
|
{
|
|
$this->apiServer = $server;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* This will return a response with code 400. Usage example:
|
|
* return $this->createBadRequestResponse('Unable to access this page!');
|
|
*
|
|
* @param string $message A message
|
|
*
|
|
* @return Response
|
|
*/
|
|
public function createBadRequestResponse(string $message = 'Bad Request.'): Response
|
|
{
|
|
return new Response($message, 400);
|
|
}
|
|
|
|
/**
|
|
* This will return an error response. Usage example:
|
|
* return $this->createErrorResponse(new UnauthorizedHttpException());
|
|
*
|
|
* @param HttpException $exception An HTTP exception
|
|
*
|
|
* @return Response
|
|
*/
|
|
public function createErrorResponse(HttpException $exception): Response
|
|
{
|
|
$statusCode = $exception->getStatusCode();
|
|
$headers = array_merge($exception->getHeaders(), ['Content-Type' => 'application/json']);
|
|
|
|
$json = $this->exceptionToArray($exception);
|
|
$json['statusCode'] = $statusCode;
|
|
|
|
return new Response(json_encode($json, 15), $statusCode, $headers);
|
|
}
|
|
|
|
/**
|
|
* Serializes data to a given type format.
|
|
*
|
|
* @param mixed $data The data to serialize.
|
|
* @param string $format The target serialization format.
|
|
*
|
|
* @return string A serialized data string.
|
|
*/
|
|
protected function serialize($data, string $format): string
|
|
{
|
|
return $this->serializer->serialize($data, $format);
|
|
}
|
|
|
|
/**
|
|
* Deserializes data from a given type format.
|
|
*
|
|
* @param mixed $data The data to deserialize.
|
|
* @param string $class The target data class.
|
|
* @param string $format The source serialization format.
|
|
*
|
|
* @return mixed A deserialized data.
|
|
*/
|
|
protected function deserialize($data, string $class, string $format)
|
|
{
|
|
return $this->serializer->deserialize($data, $class, $format);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $data
|
|
* @param mixed $asserts
|
|
*
|
|
* @return Response|null
|
|
*/
|
|
protected function validate($data, $asserts = null): ?Response
|
|
{
|
|
$errors = $this->validator->validate($data, $asserts);
|
|
|
|
if (count($errors) > 0) {
|
|
$errorsString = '';
|
|
/** @var ConstraintViolation $violation */
|
|
foreach ($errors as $violation) {
|
|
$errorsString .= $violation->getMessage()."\n";
|
|
}
|
|
return $this->createBadRequestResponse($errorsString);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Converts an exception to a serializable array.
|
|
*
|
|
* @param \Throwable|null $exception
|
|
*
|
|
* @return array|null
|
|
*/
|
|
private function exceptionToArray(?\Throwable $exception = null): ?array
|
|
{
|
|
if (null === $exception) {
|
|
return null;
|
|
}
|
|
|
|
if (!$this->container->get('kernel')->isDebug()) {
|
|
return [
|
|
'message' => $exception->getMessage(),
|
|
];
|
|
}
|
|
|
|
return [
|
|
'message' => $exception->getMessage(),
|
|
'type' => get_class($exception),
|
|
'previous' => $this->exceptionToArray($exception->getPrevious()),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Converts an exception to a serializable array.
|
|
*
|
|
* @param string $accept
|
|
* @param array $produced
|
|
*
|
|
* @return ?string
|
|
*/
|
|
protected function getOutputFormat(string $accept, array $produced): ?string
|
|
{
|
|
// First get the list of formats accepted by the client by weight.
|
|
// eg: text/html,*/*; q=0.7, text/*;q=0.8,text/plain;format=fixed;q=0.6 is turned to
|
|
// [
|
|
// text/html => 1, // because when no weight is present then the default value is 1. See https://httpwg.org/specs/rfc9110.html#quality.values .
|
|
// */* => 0.7,
|
|
// text/* => 0.8,
|
|
// text/plain => 0.6,
|
|
// ]
|
|
// So we can subsequently order that list by descending weight (because 1 is the most prefered value, 0.001 is the least preferred)
|
|
$weightedFormats = array();
|
|
foreach (explode(",", str_replace(' ', '', $accept)) as $accept) {
|
|
$exploded = explode(';', $accept);
|
|
|
|
// If no weight is present then the default value is 1 (see https://httpwg.org/specs/rfc9110.html#quality.values )
|
|
if (count($exploded) === 1) {
|
|
$weight = 1.0;
|
|
} else {
|
|
$lastItem = end($exploded);
|
|
if (str_starts_with($lastItem, "q=")) {
|
|
$weight = (float) str_replace("q=", "", $lastItem);
|
|
} else {
|
|
$weight = 1.0;
|
|
}
|
|
}
|
|
$weightedFormats[$exploded[0]] = $weight;
|
|
}
|
|
arsort($weightedFormats);
|
|
|
|
// Now return the first produced format that matches
|
|
foreach (array_keys($weightedFormats) as $acceptedFormat) {
|
|
$acceptedFormatParts = explode('/', $acceptedFormat);
|
|
if (count($acceptedFormatParts) != 2) {
|
|
// badly formatted header sent by the client. Let's continue (instead of crashing)
|
|
continue;
|
|
}
|
|
$acceptedFormatType = $acceptedFormatParts[0];
|
|
$acceptedFormatSubtype = $acceptedFormatParts[1];
|
|
|
|
foreach ($produced as $producedFormat) {
|
|
if ($acceptedFormat === $producedFormat) {
|
|
return $producedFormat;
|
|
}
|
|
if ($acceptedFormatSubtype === '*' && $acceptedFormatType === explode("/", $producedFormat)[0]) {
|
|
return $producedFormat;
|
|
}
|
|
if ($acceptedFormat === "*/*") {
|
|
return $producedFormat;
|
|
}
|
|
}
|
|
}
|
|
// If we reach this point, we don't have a common ground between server and client
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks whether Content-Type request header presented in supported formats.
|
|
*
|
|
* @param Request $request Request instance.
|
|
* @param array $consumes Array of supported content types.
|
|
*
|
|
* @return bool Returns true if Content-Type supported otherwise false.
|
|
*/
|
|
public static function isContentTypeAllowed(Request $request, array $consumes = []): bool
|
|
{
|
|
if (!empty($consumes) && $consumes[0] !== '*/*') {
|
|
$currentFormat = $request->getContentTypeFormat();
|
|
foreach ($consumes as $mimeType) {
|
|
// canonize mime type
|
|
if (is_string($mimeType) && false !== $pos = strpos($mimeType, ';')) {
|
|
$mimeType = trim(substr($mimeType, 0, $pos));
|
|
}
|
|
|
|
if (!$format = $request->getFormat($mimeType)) {
|
|
// add custom format to request
|
|
$format = $mimeType;
|
|
$request->setFormat($format, $format);
|
|
$currentFormat = $request->getContentTypeFormat();
|
|
}
|
|
|
|
if ($format === $currentFormat) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|