-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Routing] Redirection from route configuration #25145
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
c11e8ce
to
859bc4f
Compare
PHP-based routing configuration also needs to be patched. |
/** | ||
* Common parsing logic between route loaders. | ||
* | ||
* @author Samuel Roze <samuel.roze@gmail.com> |
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.
@internal
also
@nicolas-grekas we did not add the |
$defaults['_controller'] = 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction'; | ||
$defaults['path'] = $redirect; | ||
} else { | ||
$defaults['_controller'] = 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction'; |
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.
But there is a big glitch here: this is in the Routing
namespace, and it references something in FrameworkBundle
.
We have to fix that.
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.
Good point. For the records, here's @javiereguiluz's answer when @stof's said the same thing. I need to think a bit about it.
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 see a few options:
- A simple
class_exists
check on theRedirectController
to have a user-friendly exception. This references the FrameworkBundle, but is not a hard coupling. - We use a
_redirect
attribute, that will be handled by aControllerResolverInterface
implementation within the FrameworkBundle. - We use a
_redirect
attribute that will be handled by a listener onkernel.request
within theHttpKernel
component
Right, so you mean we should add a |
private function looksLikeUrl(string $urlOrRouteName): bool | ||
{ | ||
foreach (array('/', '//', 'http://', 'https://') as $pattern) { | ||
if (substr($urlOrRouteName, 0, strlen($pattern)) == $pattern) { |
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.
should use 0 === strpos()
instead to be consistent with the codebase (we avoid allocating a new string)
private function redirectToDefaults(string $redirect, bool $permanent = false): array | ||
{ | ||
if ($this->looksLikeUrl($redirect)) { | ||
$defaults['_controller'] = 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction'; |
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 be better to write it as $defaults = array(..., ...)
instead of assigning in an undefined variable
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.
Not sure how that happened - well, I know, refactoring.. - , I'd expect PHP to tell me how bad was that.
@@ -239,6 +241,10 @@ private function parseConfigs(\DOMElement $node, $path) | |||
$defaults['_controller'] = $controller; | |||
} | |||
|
|||
if ($redirectTo = $node->getAttribute('redirect-to')) { |
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.
should forbid to put it at the same time than a controller (check for $defaults['_controller']
being configured already)
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.
Done 👍
@@ -239,6 +241,10 @@ private function parseConfigs(\DOMElement $node, $path) | |||
$defaults['_controller'] = $controller; | |||
} | |||
|
|||
if ($redirectTo = $node->getAttribute('redirect-to')) { | |||
$defaults += $this->redirectToDefaults($redirectTo, $node->getAttribute('redirect-permanent')); |
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.
not good. The attribute value will always be a string. It must be parsed into an actual boolean (see the values accepted by parseDefaultNode
for the <bool>
tag)
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.
But.. it's working well... not sure getAttribute
actually returns only string
s 🤔
As stated in #24640 (comment) and below, I would do it differently: instead of setting a controller, the |
@nicolas-grekas definitely agree, see my proposed options here. |
return $defaults; | ||
} | ||
|
||
private function looksLikeUrl(string $urlOrRouteName): bool |
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 fear any guessing strategy would be fragile. Instead, what about two defaults:
_redirect_to_route
_redirect_to_uri
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 don't think this is a valid fear: if the string starts with /
or a [scheme]://
then it's an URL seems enough to me. Having two ways of configuring the "target" reduces the DX.
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.
Moreover, we already do that guessing in other parts of the framework. E.g. the login_path
option in the always delicate Security component.
The real question now is: who is going to handle the |
Adding one more question we need to answer: |
I'd say |
0fa3e8e
to
484121e
Compare
@stof @nicolas-grekas I've updated the PR to use |
@sroze what would be the new way of doing this configuration in yaml? Can we do without a listener (yet another one for this looks like overhead to me...) |
The one in the PR description :)
Well... we could. Do you want me to merge with the existing one or doing something else? 🤔 |
62e7de8
to
5a35a7f
Compare
8edd537
to
50f9243
Compare
AppVeyor's failure is not related. It's a network issue. Can somebody restart it? |
ping @Tobion :) |
50f9243
to
3266887
Compare
Now that I know the router a bit better, I'd suggest leveraging |
Can you guide me through what you have in mind a little bit more? Maybe one way would be to roughly explain how you'd change this pull-request? |
when a |
I've been thinking about this proposal ... and I propose to make a minor change. Instead of using # redirect_to for absolute or relative URLs
blog_show:
path: /blog/{slug}
redirect_to: /some-relative-url
permanent: true
# redirect_to_route without route arguments
blog_show:
path: /blog/{slug}
redirect_to_route: 'some-other-route'
# this also works, to be consistent with the next case:
# redirect_to_route: ['some-other-route']
# redirect_to_route with route arguments
blog_show:
path: /blog/{slug}
redirect_to_route: ['some-other-route', { arg1: 'value1', arg2: 'value2' }] |
@sroze I know you are very busy and you also have a much more important PR related to the new Message component ... but it'd be great if you could finish this feature before the Symfony 4.1 "feature freeze" starts in 12 days. Thanks! |
@javiereguiluz yep, agree with you, it would be nice. I'll update the PR this week :) |
I've explored the option of moving this logic directly within the router, especially within the The problem with the current implementation is that is it specific to a few details about the routing and is sort of between the Routing component and the FrameworkBundle bundle. Redirection configuration (while allowed by the Routing component) wouldn't work without the FrameworkBundle, which is not what we'd like. While discussing all of that with @nicolas-grekas, we jumped back to actually... what we are trying to solve: simplifying the YAML configuration. It might be that the only issue, for now, is the redirect_to_admin:
path: /
defaults:
_controller: _redirect_path
path: /admin
permanent: true What do you all think? |
I haven't looked at the internals ... but from the outside this looks "just" a syntactic sugar that should be "easy" to implement. So, the end-user writes this in YAML: blog_show:
path: /blog/{slug}
redirect_to: /some-relative-url
permanent: true And Symfony, when parsing that YAML file, transforms it to this transparently: blog_show:
path: /blog/{slug}
defaults:
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
path: /some-relative-url
permanent: true So, this feature "just" needs to look for a |
@javiereguiluz the real problem is that:
|
@sroze this is how I imagine the implementation of this feature (the code doesn't work, I just invented it to show what I'm asking for): public function processRoutes($yamlContent)
{
$routesConfig = Yaml::parse($yamlContent);
foreach ($routesConfig as $routeConfig) {
+ if (isset($routeConfig['redirect_to'])) {
+ $routeConfig['_defaults']['_controller'] = 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction';
+ $routeConfig['_defaults']['path'] = $routeConfig['redirect_to'];
+ unset($routeConfig['redirect_to']);
+ }
// ...
}
return $routeCollection;
} |
Yes, I agree with you. But it would mean the Routing component depends on the FrameworkBundle (at least, the way it is now). |
I'm now convinced the aliasing feature is all that is needed at least as a first step. This should be a feature of RouterListener in HttpKernel IMHO (could also be in Routing matchers, but I feel better with RouterListener). So down this path, we would just need a Javier has another goal, which is to move defaults out of the "defaults" nesting level. All-in-all, the controller aliasing looks like the right next step to me. |
To better compare the alternatives, here is a summary: Current situationblog_show:
path: /blog/{slug}
defaults:
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
path: /some-relative-url
permanent: true Proposal 1blog_show:
path: /blog/{slug}
redirect_to: /some-relative-url
permanent: true Proposal 2blog_show:
path: /blog/{slug}
defaults:
_controller: _redirect_path
path: /some-relative-url
permanent: true I think it's OK to create a |
I'm 👎 for adding redirect specific root-options because it creates a very specific case people will have to learn, whereas the proposed alternative (2) just leverages existing and new generic knowledge. The redirect-specific entries would also prevent using new or exotic redirection options, because these are specific to the actual redirector implementation. |
The only counter-argument to @nicolas-grekas' PoV I can see is that a redirection is not a "very specific case" of routing. |
{ | ||
$requestAttributes = $event->getRequest()->attributes; | ||
|
||
if (!$requestAttributes->has('_controller') && $redirectTo = $requestAttributes->get('_redirect_to')) { |
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 would use early returns instead:
if ($requestAttributes->has('_controller')) {
return;
}
if (!$redirectTo = $requestAttributes->get('_redirect_to')) {
return;
}
); | ||
} | ||
|
||
public function onKernelRequest(GetResponseEvent $event) |
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.
public function onKernelRequest(GetResponseEvent $event): void
*/ | ||
class ResolveRedirectControllerSubscriber implements EventSubscriberInterface | ||
{ | ||
public static function getSubscribedEvents() |
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.
public static function getSubscribedEvents(): array
|
Let's close this one, as this direction is a dead end :) |
…le template and redirect controllers (HeahDude) This PR was merged into the 5.1-dev branch. Discussion ---------- [FrameworkBundle][Routing] added Configurators to handle template and redirect controllers | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | let's see | Fixed tickets | partially #24640, #25145 | License | MIT | Doc PR | symfony/symfony-docs#11120 While working on symfony/symfony-docs#11085, I felt bad about the long notations required for simple [redirects](https://symfony.com/doc/current/routing/redirect_in_config.html) and [templates rendering](https://symfony.com/doc/current/templating/render_without_controller.html) template actions, but I love and use those features since always. Then I gave it a try yesterday night and now I realised I missed #24640 and that #25145 has been closed x). So here we go, here's my WIP. WDYT of this implementation? ping @javiereguiluz? I'm going to open the PR in the docs so we can discuss the DX changes there too, and keep focus on the code here. Cheers! EDIT ---- This PR now only update PHP-DSL configurators. ______________ TODO: - [x] gather reviews - ~[x] fix xml schema~ - [x] add some tests - ~[ ] handle xsd auto discovery~ - [x] rebase on top of #30507 - [x] ~add shortcuts for #30514~ Commits ------- de74794 [FrameworkBundle][Routing] added Configurators to handle template and redirect controllers
Following @javiereguiluz's proposal (#24640), this PR allows developers to configure the redirection directly in the routing configuration.