Skip to content

Commit 16345b6

Browse files
authored
fix(router): content-type json support when mapping psr request to tempest request (#956)
1 parent 4f3a5be commit 16345b6

File tree

4 files changed

+46
-0
lines changed

4 files changed

+46
-0
lines changed

src/Tempest/Router/src/Mappers/PsrRequestToRequestMapper.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Tempest\Router\Upload;
1414
use Tempest\Validation\Validator;
1515
use function Tempest\map;
16+
use function Tempest\Support\arr;
1617

1718
final readonly class PsrRequestToRequestMapper implements Mapper
1819
{
@@ -33,6 +34,14 @@ public function map(mixed $from, mixed $to): array|object
3334

3435
$data = (array) $from->getParsedBody();
3536

37+
if (arr($from->getHeader('content-type'))->contains('application/json')) {
38+
$bodyContents = $from->getBody()->getContents();
39+
40+
if (json_validate($bodyContents)) {
41+
$data = [...$data, ...json_decode($bodyContents, true)];
42+
}
43+
}
44+
3645
$headersAsString = array_map(
3746
fn (array $items) => implode(',', $items),
3847
$from->getHeaders(),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Tests\Tempest\Fixtures\Controllers;
4+
5+
use Tempest\Router\Post;
6+
use Tempest\Router\Request;
7+
use Tempest\Router\Responses\Ok;
8+
9+
final class JsonController
10+
{
11+
#[Post('/json-endpoint')]
12+
public function __invoke(Request $request)
13+
{
14+
return new Ok($request->get('title'));
15+
}
16+
}

tests/Integration/Route/RouterTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Tests\Tempest\Integration\Route;
66

7+
use Laminas\Diactoros\ServerRequest;
8+
use Laminas\Diactoros\Stream;
9+
use Laminas\Diactoros\Uri;
710
use Tempest\Core\AppConfig;
811
use Tempest\Database\Migrations\CreateMigrationsTable;
912
use Tempest\Http\Status;
@@ -188,4 +191,21 @@ public function test_uri_with_query_param_that_collides_partially_with_route_par
188191
uri([UriGeneratorController::class, 'withCollidingNames'], id: '1', idea: 'hi'),
189192
);
190193
}
194+
195+
public function test_json_request(): void
196+
{
197+
$router = $this->container->get(Router::class);
198+
199+
$response = $router->dispatch(new ServerRequest(
200+
uri: new Uri('/json-endpoint'),
201+
method: 'POST',
202+
body: new Stream(fopen(__DIR__ . '/request.json', 'r')),
203+
headers: [
204+
'Content-Type' => 'application/json',
205+
],
206+
));
207+
208+
$this->assertSame(Status::OK, $response->status);
209+
$this->assertSame('foo', $response->body);
210+
}
191211
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"title": "foo"}

0 commit comments

Comments
 (0)