diff --git a/module/Core/src/Exception/MalformedBodyException.php b/module/Core/src/Exception/MalformedBodyException.php new file mode 100644 index 00000000..941730d1 --- /dev/null +++ b/module/Core/src/Exception/MalformedBodyException.php @@ -0,0 +1,29 @@ +detail = $e->getMessage(); + $e->title = 'Malformed request body'; + $e->type = toProblemDetailsType('malformed-request-body'); + $e->status = StatusCodeInterface::STATUS_BAD_REQUEST; + + return $e; + } +} diff --git a/module/Core/test/Exception/MalformedBodyExceptionTest.php b/module/Core/test/Exception/MalformedBodyExceptionTest.php new file mode 100644 index 00000000..ecccfdf2 --- /dev/null +++ b/module/Core/test/Exception/MalformedBodyExceptionTest.php @@ -0,0 +1,27 @@ +getPrevious()); + self::assertEquals('Provided request does not contain a valid JSON body.', $e->getMessage()); + self::assertEquals('Provided request does not contain a valid JSON body.', $e->getDetail()); + self::assertEquals('Malformed request body', $e->getTitle()); + self::assertEquals('https://shlink.io/api/error/malformed-request-body', $e->getType()); + self::assertEquals(StatusCodeInterface::STATUS_BAD_REQUEST, $e->getStatus()); + } +} diff --git a/module/Rest/src/Middleware/BodyParserMiddleware.php b/module/Rest/src/Middleware/BodyParserMiddleware.php index 8922de03..68fc1b38 100644 --- a/module/Rest/src/Middleware/BodyParserMiddleware.php +++ b/module/Rest/src/Middleware/BodyParserMiddleware.php @@ -5,10 +5,12 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Rest\Middleware; use Fig\Http\Message\RequestMethodInterface; +use JsonException; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Exception\MalformedBodyException; use function Functional\contains; use function Shlinkio\Shlink\Common\json_decode; @@ -42,7 +44,11 @@ class BodyParserMiddleware implements MiddlewareInterface, RequestMethodInterfac return $request; } - $parsedJson = json_decode($rawBody); - return $request->withParsedBody($parsedJson); + try { + $parsedJson = json_decode($rawBody); + return $request->withParsedBody($parsedJson); + } catch (JsonException $e) { + throw MalformedBodyException::forInvalidJson($e); + } } } diff --git a/module/Rest/test/Middleware/BodyParserMiddlewareTest.php b/module/Rest/test/Middleware/BodyParserMiddlewareTest.php index 63354a76..429a35ea 100644 --- a/module/Rest/test/Middleware/BodyParserMiddlewareTest.php +++ b/module/Rest/test/Middleware/BodyParserMiddlewareTest.php @@ -7,10 +7,12 @@ namespace ShlinkioTest\Shlink\Rest\Middleware; use Laminas\Diactoros\Response; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\Stream; +use PHPUnit\Framework\Assert; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Exception\MalformedBodyException; use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware; class BodyParserMiddlewareTest extends TestCase @@ -65,7 +67,6 @@ class BodyParserMiddlewareTest extends TestCase /** @test */ public function jsonRequestsAreJsonDecoded(): void { - $test = $this; $body = new Stream('php://temp', 'wr'); $body->write('{"foo": "bar", "bar": ["one", 5]}'); $request = (new ServerRequest())->withMethod('PUT') @@ -73,16 +74,31 @@ class BodyParserMiddlewareTest extends TestCase $handler = $this->createMock(RequestHandlerInterface::class); $handler->expects($this->once())->method('handle')->with( $this->isInstanceOf(ServerRequestInterface::class), - )->willReturnCallback( - function (ServerRequestInterface $req) use ($test) { - $test->assertEquals([ - 'foo' => 'bar', - 'bar' => ['one', 5], - ], $req->getParsedBody()); + )->willReturnCallback(function (ServerRequestInterface $req) { + Assert::assertEquals([ + 'foo' => 'bar', + 'bar' => ['one', 5], + ], $req->getParsedBody()); - return new Response(); - }, - ); + return new Response(); + }); + + $this->middleware->process($request, $handler); + } + + /** @test */ + public function invalidBodyResultsInException(): void + { + $body = new Stream('php://temp', 'wr'); + $body->write('{"foo": "bar", "bar": ["one'); + $request = (new ServerRequest())->withMethod('PUT') + ->withBody($body); + + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->never())->method('handle'); + + $this->expectException(MalformedBodyException::class); + $this->expectExceptionMessage('Provided request does not contain a valid JSON body.'); $this->middleware->process($request, $handler); }