Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.6k
[HttpKernel] Adding new#[MapRequestHeader]
attribute and resolver#51379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:7.4
Are you sure you want to change the base?
Conversation
carsonbot commentedAug 14, 2023
Hey! I see that this is your first PR. That is great! Welcome! Symfony has acontribution guide which I suggest you to read. In short:
Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change. When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor! I am going to sit back now and wait for the reviews. Cheers! Carsonbot |
527c461
toe85136d
CompareAurelienPillevesse commentedAug 14, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Maybe I'm wrong but seems that something related to symfony/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php Line 84 in16a4681
Why did not add in the same file? |
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.phpShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
robert-sykes left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Would this not be best to also allow it to map to an object, like how theMapRequestPayload
currently works, as I can pass anObject
as a reference the serializer will deserialize to this object? That way you can have aHeader
class that the headers deserialize into.
You can merge it with your current concept and have a bit of best of both? Map individual Headers to an string, or an object or even just an array if required?
Something potentially like this?
privatefunctionmapRequestHeader(Request$request,string$type,MapRequestHeader$attribute): ?object {$headers = [];$requestHeaders =$request->headers->all();array_walk($requestHeaders,function ($value,$key)use (&$headers) {$key =$this->snakeCaseToCamelCase($key);$headers[$key] =$value[0]; });$headers =json_encode($headers);$format ='json';try {return$this->serializer->deserialize($headers,$type,$format, []); }catch (UnsupportedFormatException$e) {thrownewHttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE,sprintf('Unsupported format: "%s".',$format),$e); }catch (NotEncodableValueException$e) {thrownewHttpException(Response::HTTP_BAD_REQUEST,sprintf('Request payload contains invalid "%s" data.',$format),$e); } }
and you could have a class like this:
class HeaderRequest{publicfunction__construct(publicreadonlystring$userAgent,publicreadonlystring$accept,publicreadonlystring$contentType,publicreadonlystring$acceptEncoding, ) { }}
An advantage is you can use the Validator too and enforce any header types if needed or create a HeaderRequest class per request as needed.
You can add this as a new method to the existingRequestPayloadValueResolver
as already mentioned by@AurelienPillevesse so it will tie nicely into the existing process.
This is a rough example but just food for thought.
Very useful PR this and something we need, so looking forward to seeing how it progresses :)
261f6e6
to72cb1ee
Comparesrc/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
afbdd35
to90f1015
Comparesrc/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
ruudk commentedAug 21, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Too bad the PR was changed after@robert-sykes suggestion. I think these are 2 distinct ideas. The original PR description mentions a single attribute per header, same as how MapQueryParameter works. There are valid cases for that, where you only want and need a single header scalar. The updated code now requires you to map a header to a header object. Would be great to also keep supporting the original one for just a single header. See this comment where a similar situation happened:#49134 (comment) |
robert-sykes commentedAug 21, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'm all for this, to be fair. I did imply this in my original comment where I said @StevenRenaux You could check the type and if it's a string map to that similar to your original commit, otherwise if it's object map to those fields instead. We get best of both worlds this way. What do you think? |
StevenRenaux commentedAug 21, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey, I will try to do it and I didn't understand well for sure but we are there to share and go further 😉 |
robert-sykes commentedAug 21, 2023
No problem at all. If you need a hand let me know, more than happy to pair on this with you. :) |
ff6b41f
to0bda0bf
CompareStevenRenaux commentedAug 22, 2023 • edited by OskarStark
Loading Uh oh!
There was an error while loading.Please reload this page.
edited by OskarStark
Uh oh!
There was an error while loading.Please reload this page.
To get back to the original Issue I suggest this namespaceApp\Controller;class TestControllerextends AbstractController{ #[Route(path:'/', name:'test')]publicfunctiontest( #[MapRequestHeader]HeaderRequestDto$headerRequestDto, #[MapRequestHeader(name:'user-agent')]string$agent, #[MapRequestHeader]array$accept ):Response {dd($headerRequestDto,// App\Entity\HeaderRequestDto {// accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"// agent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0"// acceptEncoding: "gzip, deflate, br"// host: "localhost:8000"// }$agent,// "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0"$accept// array:6 [// 0 => "text/html"// 1 => "application/xhtml+xml"// 2 => "application/xml;q=0.9"// 3 => "image/avif"// 4 => "image/webp"// 5 => "*/*;q=0.8"// ] ); }} And a Dto could look like this : namespaceApp\Entity;useSymfony\Component\Serializer\Annotation\SerializedName;useSymfony\Component\Validator\ConstraintsasAssert;class HeaderRequestDto{publicfunction__construct(publicreadonlystring$accept, #[Assert\EqualTo('test', groups: ['test'])] #[SerializedName('user-agent')]publicreadonlystring$agent ='agent', #[SerializedName('accept-encoding')]publicreadonlystring$acceptEncoding, #[Assert\EqualTo('localhost', groups: ['valid'])]publicreadonlystring$host ) { }} We only use kebab-case (as returned by HeaderBag) now to get some property like 'user-agent'. |
0bda0bf
tocced8c1
CompareHi@StevenRenaux! What's the current state of this PR? 🙂 |
@alexandre-daubois Waiting for review, since the last requested update. |
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestHeaderValueResolver.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
#[MapRequestHeader]
and#[MapRequestHeaders]
attributes and resolver#[MapRequestHeader]
attribute and resolver7494a85
to0f38cde
CompareStevenRenaux commentedJan 7, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Back to the basics of PR, using only the MapRequestHeader attribute. Mapping to a DTO does not seem like a relevant or common use case or if it is really necessary could be done in a future PR. The allowed types are therefore:
When the header parameter cannot be determined by the variable name, we can specify the name argument in the attribute. publicfunctiontest( #[MapRequestHeader('user-agent')] ?string$bar,):Response{dump($bar);// "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"} If we are looking for a header of type Accept-* with an array type, we will use the dedicated methods to retrieve this data. https://symfony.com/doc/current/components/http_foundation.html#accessing-accept-headers-data publicfunctiontest( #[MapRequestHeader]array$accept,):Response{dump($accept);// array:7 [▼// 0 => "text/html"// 1 => "application/xhtml+xml"// 2 => "image/avif"// 3 => "image/webp"// 4 => "image/apng"// 5 => "application/xml"// 6 => "*/*"// ]} And with AcceptHeader type publicfunctiontest( #[MapRequestHeader(name:'accept-encoding')]AcceptHeader$encoding,):Response{dump($encoding);// Symfony\Component\HttpFoundation\AcceptHeader {#4727 ▼// -items: array:4 [▼// "gzip" =>// Symfony\Component\HttpFoundation\AcceptHeaderItem {#2437 ▼// -value: "gzip"// -quality: 1.0// -index: 0// -attributes: []// }// "deflate" =>// Symfony\Component\HttpFoundation\AcceptHeaderItem {#4882 ▼// -value: "deflate"// -quality: 1.0// -index: 1// -attributes: []// }// "br" =>// Symfony\Component\HttpFoundation\AcceptHeaderItem {#4549 ▼// -value: "br"// -quality: 1.0// -index: 2// -attributes: []// }// "zstd" =>// Symfony\Component\HttpFoundation\AcceptHeaderItem {#4773 ▼// -value: "zstd"// -quality: 1.0// -index: 3// -attributes: []// }// ]// -sorted: false// }} |
fbde828
to89f420f
CompareIt bugs me the
|
@StevenRenaux What's needed to get this to a mergeable state? I would love this to be included in the new Symfony version. |
@ruudk Waiting reviews from core-contributors, but it's seems to late for merging into the next 7.3 version. |
Could you rebase it, so that the tests go green? |
89f420f
to2142045
Compare@StevenRenaux Thanks! 🙏 It needs a few tweaks inhttps://github.com/symfony/symfony/actions/runs/15253101552/job/42894283925?pr=51379 |
2142045
to7a123ea
Compare'accept-charset' => $request->getCharsets(), | ||
'accept-language' => $request->getLanguages(), | ||
'accept-encoding' => $request->getEncodings(), | ||
default => [$request->headers->get($name)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
default =>[$request->headers->get($name)], | |
default =>$request->headers->all($name), |
} | ||
if (null === $value && !$argument->isNullable()) { | ||
throw new HttpException($attribute->validationFailedStatusCode, \sprintf('Missing header "%s".', $name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think if type is array we could return an empty array
Uh oh!
There was an error while loading.Please reload this page.
Hi,
Related to this#51342 I tried to work on this PR.
To use this new attribute, you just need to add it in your method of your controller.
For some parameters as
User-Agent
we need to add the name for the resolver, because the conversion ofuserAgent
is not interpreted by the resolver and return an exception as Missing header parameter "userAgent".And for some others parameters as cookie I don't know what to do, because at the moment it only return string for all of them and is it really useful 🤷