Skip to content

Commit e85136d

Browse files
committed
Adding a new Attribute MapRequestHeader class and resolver
1 parent 16a4681 commit e85136d

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver;
1919
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\QueryParameterValueResolver;
2020
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver;
21+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestHeaderValueResolver;
2122
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver;
2223
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
2324
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver;
@@ -97,6 +98,9 @@
9798
->set('argument_resolver.query_parameter_value_resolver', QueryParameterValueResolver::class)
9899
->tag('controller.targeted_value_resolver', ['name' => QueryParameterValueResolver::class])
99100

101+
->set('argument_resolver.header_value_resolver', RequestHeaderValueResolver::class)
102+
->tag('controller.targeted_value_resolver', ['name' => RequestHeaderValueResolver::class])
103+
100104
->set('response_listener', ResponseListener::class)
101105
->args([
102106
param('kernel.charset'),
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Attribute;
13+
14+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestHeaderValueResolver;
15+
16+
#[\Attribute(\Attribute::TARGET_PARAMETER)]
17+
class MapRequestHeader extends ValueResolver
18+
{
19+
public function __construct(
20+
public ?string $name = null,
21+
string $resolver = RequestHeaderValueResolver::class,
22+
) {
23+
parent::__construct($resolver);
24+
}
25+
}

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* Add argument `$validationFailedStatusCode` to `#[MapQueryString]` and `#[MapRequestPayload]`
1212
* Add argument `$debug` to `Logger`
1313
* Add class `DebugLoggerConfigurator`
14+
* Add `#[MapRequestHeader]` to map and validate request header from `Request::$headers` to typed objects
1415

1516
6.3
1617
---
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpKernel\Attribute\MapRequestHeader;
16+
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
17+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
18+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
19+
20+
final class RequestHeaderValueResolver implements ValueResolverInterface
21+
{
22+
public function resolve(Request $request, ArgumentMetadata $argument): iterable
23+
{
24+
$header = $request->headers;
25+
26+
if (!$attribute = $argument->getAttributesOfType(MapRequestHeader::class)[0] ?? null) {
27+
return [];
28+
}
29+
30+
$name = $attribute->name ?? $argument->getName();
31+
32+
if (!$header->has($name)) {
33+
if ($argument->isNullable() || $argument->hasDefaultValue()) {
34+
return [];
35+
}
36+
37+
throw new NotFoundHttpException(sprintf('Missing header parameter "%s".', $name));
38+
}
39+
40+
return [$header->get($name)];
41+
}
42+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpKernel\Attribute\MapRequestHeader;
17+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestHeaderValueResolver;
18+
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
19+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
20+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21+
22+
class RequestHeaderValueResolverTest extends TestCase
23+
{
24+
private ValueResolverInterface $resolver;
25+
26+
protected function setUp(): void
27+
{
28+
$this->resolver = new RequestHeaderValueResolver();
29+
}
30+
31+
public function testHeaderParameter()
32+
{
33+
$allParameters = [
34+
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
35+
'Accept-Language' => 'en-us,en;q=0.5',
36+
'Host' => 'localhost',
37+
'User-Agent' => 'Symfony',
38+
];
39+
40+
foreach ($allParameters as $parameter => $value) {
41+
$metadata = new ArgumentMetadata('variableName', 'string', false, false, null, false, [
42+
MapRequestHeader::class => new MapRequestHeader($parameter),
43+
]);
44+
45+
$arguments = $this->resolver->resolve(Request::create('/'), $metadata);
46+
47+
self::assertEquals([$value], $arguments);
48+
}
49+
}
50+
51+
public function testHeaderParameterIsMissing()
52+
{
53+
$metadata = new ArgumentMetadata('variableName', 'string', false, false, null, false, [
54+
MapRequestHeader::class => new MapRequestHeader(),
55+
]);
56+
57+
try {
58+
$this->resolver->resolve(Request::create('/'), $metadata);
59+
} catch (NotFoundHttpException $exception) {
60+
self::assertEquals('Missing header parameter "variableName".', $exception->getMessage());
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)