Add JSON schema validation feature.
This commit is contained in:
parent
a385f4c134
commit
c51d9b1cf6
5 changed files with 145 additions and 1 deletions
|
|
@ -62,6 +62,7 @@
|
|||
"slim/slim": "^4.5",
|
||||
"slim/twig-view": "^3.2",
|
||||
"squizlabs/php_codesniffer": "3.*",
|
||||
"swaggest/json-schema": "^0.12.39",
|
||||
"symfony/translation": "^5.1",
|
||||
"symfony/twig-bridge": "^5.1",
|
||||
"symfony/yaml": "^5.1",
|
||||
|
|
|
|||
21
src/Annotations/JsonSchema.php
Normal file
21
src/Annotations/JsonSchema.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Benzine\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class JsonSchema
|
||||
{
|
||||
/**
|
||||
* @Required
|
||||
*/
|
||||
public string $schema;
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
namespace Benzine;
|
||||
|
||||
use Benzine\Exceptions\JsonErrorHandler;
|
||||
use Benzine\Middleware\JsonResponseExecTimeMiddleware;
|
||||
use Benzine\Middleware\JsonValidationMiddleware;
|
||||
use Benzine\ORM\Connection\Databases;
|
||||
use Benzine\ORM\Laminator;
|
||||
use Benzine\Redis\Redis;
|
||||
|
|
@ -97,10 +99,14 @@ class App
|
|||
// Configure Slim
|
||||
$this->app = AppFactory::create();
|
||||
$this->app->add(Slim\Views\TwigMiddleware::createFromContainer($this->app));
|
||||
$this->app->addRoutingMiddleware();
|
||||
|
||||
$this->setupMiddlewares($container);
|
||||
|
||||
$this->app->add($container->get(JsonValidationMiddleware::class));
|
||||
$this->app->addBodyParsingMiddleware();
|
||||
$this->app->addRoutingMiddleware();
|
||||
$this->app->add($container->get(JsonResponseExecTimeMiddleware::class));
|
||||
|
||||
// Determine if we're going to enable debug mode
|
||||
$this->debugMode = $this->environmentService->get('DEBUG_MODE', 'off') == 'on';
|
||||
|
||||
|
|
|
|||
44
src/Middleware/JsonResponseExecTimeMiddleware.php
Normal file
44
src/Middleware/JsonResponseExecTimeMiddleware.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Benzine\Annotations\JsonSchema;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Slim\Psr7\Factory\StreamFactory;
|
||||
use Swaggest\JsonSchema\Exception;
|
||||
use Swaggest\JsonSchema\Schema;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Slim\Psr7\Request;
|
||||
use Slim\Psr7\Response;
|
||||
use Slim\Routing\RouteContext;
|
||||
|
||||
class JsonResponseExecTimeMiddleware implements MiddlewareInterface{
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
|
||||
$response = $handler->handle($request);
|
||||
$response->getBody()->rewind();
|
||||
$responseJson = json_decode($response->getBody()->getContents(), true);
|
||||
if($responseJson === null){
|
||||
|
||||
return $response;
|
||||
}
|
||||
$responseJson['Exec'] = [
|
||||
'TimeSeconds' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
|
||||
'MemoryBytes' => memory_get_peak_usage(),
|
||||
];
|
||||
|
||||
$replacementResponse = new Response();
|
||||
$replacementResponse->getBody()->write(json_encode($responseJson, JSON_PRETTY_PRINT));
|
||||
|
||||
$replacementResponse = $replacementResponse->withHeader('Content-type', 'application/json');
|
||||
|
||||
$replacementResponse = $replacementResponse->withStatus($response->getStatusCode());
|
||||
|
||||
return $replacementResponse;
|
||||
}
|
||||
}
|
||||
72
src/Middleware/JsonValidationMiddleware.php
Normal file
72
src/Middleware/JsonValidationMiddleware.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Middleware;
|
||||
|
||||
use Benzine\Annotations\JsonSchema;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Swaggest\JsonSchema\Exception;
|
||||
use Swaggest\JsonSchema\Schema;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Slim\Psr7\Request;
|
||||
use Slim\Psr7\Response;
|
||||
use Slim\Routing\RouteContext;
|
||||
|
||||
class JsonValidationMiddleware implements MiddlewareInterface{
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// get the route out of the router...
|
||||
$route = RouteContext::fromRequest($request)->getRoute();
|
||||
|
||||
// Load an annotation reader
|
||||
$reader = new AnnotationReader();
|
||||
// Break up our route into class & method
|
||||
list ($class, $method) = explode(":", $route->getCallable());
|
||||
// Create the reflection class for our class..
|
||||
$rc = new \ReflectionClass($class);
|
||||
// .. And snag the method
|
||||
$method = $rc->getMethod($method);
|
||||
|
||||
// Try to read a json schema annotation..
|
||||
$jsonSchemaAnnotation = $reader->getMethodAnnotation($method, JsonSchema::class);
|
||||
// No annotation? Return early.
|
||||
if(!($jsonSchemaAnnotation instanceof JsonSchema)){
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
// Load the validator and the schema from disk
|
||||
$schema = Schema::import(
|
||||
json_decode(
|
||||
file_get_contents(
|
||||
$jsonSchemaAnnotation->schema
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Throw it through validation.. if it passes, continue
|
||||
try{
|
||||
// Validate it...
|
||||
$schema->in(json_decode($request->getBody()->getContents()));
|
||||
// And if we get here, we're golden.
|
||||
return $handler->handle($request);
|
||||
}catch (Exception $exception){
|
||||
// Whelp, we've failed validation, build a failure message.
|
||||
$response = new Response();
|
||||
$content = json_encode([
|
||||
'Status' => 'FAIL',
|
||||
'Reason' => "Invalid JSON, doesn't match schema!",
|
||||
'Error' => $exception->getMessage(),
|
||||
], JSON_PRETTY_PRINT);
|
||||
|
||||
$response->getBody()->write($content);
|
||||
|
||||
$response = $response->withHeader('Content-type', 'application/json');
|
||||
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue