Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Routing] configure trailing slash on imported routes #12141

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

Closed
Tobion opened this issue Oct 5, 2014 · 41 comments
Closed

[Routing] configure trailing slash on imported routes #12141

Tobion opened this issue Oct 5, 2014 · 41 comments

Comments

@Tobion
Copy link
Contributor

Tobion commented Oct 5, 2014

There is currently a known limitation when using route imports that makes it impossible to to have a root route / be imported under a prefix without a trailing slash. See #4322 and silexphp/Silex#149

I propose to solve this problem in a generic way by offering an option when importing routes to remove or add trailing slashes (which defaults to null, meaning not modifying anything):

AcmeBusinessBundle_client:
    resource: "@AcmeBusinessBundle/Resources/config/routing/client.yml"
    prefix:   /clients
    trailing_slashes: true|false|null

This way, the users can unify the path on import the way they want.

So for example

acme_business_client_list:
    path:  /
    defaults: { _controller: AcmeBusinessBundle:Client:list }

imported with trailing_slashes: false under prefix: /clients will result in path /clients instead of /clients/.

The other case with trailing_slashes: true is helpful when you import third party routes without trailing slash but you decided that you app should use training slashes everywhere consistently.

@linaori
Copy link
Contributor

linaori commented Oct 5, 2014

👍 For this suggestion

@fabpot
Copy link
Member

fabpot commented Oct 6, 2014

Are you talking about the root route only or all imported routes? I'm fine with the former, I'm not with the latter.

@Tobion
Copy link
Contributor Author

Tobion commented Oct 6, 2014

On all imported routes. What's your argument against it?

@stof
Copy link
Member

stof commented Oct 6, 2014

IMO, this should work the same than options and requirements currently: the value defined on the collection when importing would overwrite the rule inside it when we want to resolve it.

# a.yml
my_coll:
    resource: b.yml
    trailing_slashes: true
    prefix: /b

my_other_coll:
    resource: c.yml
    prefix: /c
# b.yml
first:
    path /first

my_coll:
    resource: d.yml
    trailing_slashes: false
    prefix: /d
# d.yml
second:
    path /second

third:
    path: /third/
# c.yml
fourth:
    path /fourth

my_coll:
    resource: e.yml
    trailing_slashes: false
    prefix: /e
# e.yml
fifth:
    path: /fifth/
first: /b/first/  # / enforced by the root
second: /b/d/second/ # / enforced by the root even if b asked for the opposite in the meantime as it overwrites
third: /b/d/third/
fourth: /c/fourth # left untouched as there is no requirement
fifth: /c/e/fifth # c.yml asked to remove the slash, and the root file has not chosen its own behavior

Allowing it at any even would make things much flexible than restricting at the root only, and will be consistent with the way options are forwarded currently (seeing how it applies when using the expose option for FOSJsRoutingBundle or the i18n option for JMSSerializerBundle).

The implementation could go this way btw: setting the option to false or true will alter the imported collection to either add or remove the slash. This would achieve exactly the result I'm proposing here

@Tobion
Copy link
Contributor Author

Tobion commented Oct 6, 2014

Yes that is exactly what I imagined.

@sebastianblum
Copy link

If we allow path: "" or @route(""), then we can also solve the problem with prefix and the trailing slash

/folder could be
prefix: /folder
path: ""

@Tobion
Copy link
Contributor Author

Tobion commented Oct 7, 2014

@sebastianblum there is not such thing as path == "". So it does not make sense and would only "work" when importing such a route under a prefix. But we must consider that routes can also be imported without prefix.

@sebastianblum
Copy link

@Tobion: ok, you are right.

But I would prefer to configure the trailing slash on the route and not on the prefix.

like

# prefix.yml
my_coll:
    resource: route.yml
    prefix: /folder

# route.yml
route:
    path: /
    trailing_slash: false

if you use route.yml without prefix, the route is /
with prefix, it is /folder

I would prefer this

@giosh94mhz
Copy link
Contributor

👍 on having the option added to the route as in @sebastianblum example.

If you consider the second case in @stof example:

second: /b/d/second/ # / enforced by the root even if b asked for the opposite in the meantime as it overwrites

Why it should not be the other way around? IMO, the opposite makes more sense since we are dealing with paths and not inheritance: the "root" prefer not having a trailing slash, but the "sub path" may prefer having a trailing slash. Allowing both situation will probably become complex, by requiring other options (e.g. force).

So, IMO when applied to path should only affect the root.

@Tobion
Copy link
Contributor Author

Tobion commented Feb 2, 2015

@sebastianblum it makes no sense to configure the trailing slash on the route instead of on the import.

route:
    path: /
    trailing_slash: false

makes no sense if the route in not imported under a prefix. Also it does not allow the overwrite the trailing slash of third party routes.

@dejayc
Copy link

dejayc commented Mar 12, 2015

There's another special case that we could try to find a common solution for. Some people want to specify that a route is valid regardless of whether there is a trailing slash. Consider the following:

test_route:
    path: /test

This route only matches /test, and not /test/. Here's the common way to solve the problem:

test_route:
    path: /test{ts}
requirements:
    ts: /?

This is a hacky workaround, which then complicates any optional, trailing pattern matching. Some people say, "Why not just put this logic in the web server config, so that the web server can /issue redirects/map multiple paths to the same controller/?" But if you only want certain paths to exhibit this behavior, then duplicating multiple paths in the web server config is not an ideal separation of concerns.

@dirkluijk
Copy link

I don't like the proposed solution very much, but I am dealing with the same problem. When importing (mounting) a collection of routes, I can't get rid of the trailing slash, even when path is "".

Not a world problem, but an elegant solution would be nice!

@linaori
Copy link
Contributor

linaori commented Mar 23, 2015

I would like to add 1 possible issue. The security component also triggers on redirect from without to with trailing slash. That means that if you security is configured to listen /login/check_login but your route is /login/check_login/, it will actually trigger on the /login/check_login with the redirect controller and fail. Just a small thing to take into account when suddenly importing routes with trailing slash.

@Tobion
Copy link
Contributor Author

Tobion commented Mar 23, 2015

@dirkluijk what do you not like?
@iltar what "fails"? If your security is "^/login/check_login" then it's obvious that it also matches with any suffix.

@linaori
Copy link
Contributor

linaori commented Mar 23, 2015

@Tobion Except that you do not expect it to also trigger on an internal framework redirect. It makes sense, but it's not obvious for those cases. It took me quite a few hours to figure out why a certain route with login failed.

I expected symfony to redirect with a trailing slash before the security hooks in.

@dirkluijk
Copy link

@Tobion Well, in my opinion it should work without further configuration. It just doesn't make sense to me.

This is my code in my Silex app:

$controllers
    ->get('', 'serius_catalog.controller.catalog:indexAction')
    ->bind('serius_catalog_index');

$controllers->get('/product/{productId}/{slug}', 'serius_catalog.controller.catalog:productAction')
    ->bind('serius_catalog_product');

return $controllers;
$app->mount('/catalog', new MyCatalogServiceProvider());

This should result in /catalog for serius_catalog_index, and not /catalog/.

@Tobion
Copy link
Contributor Author

Tobion commented Mar 23, 2015

Then we would need to distinguish between "" and "/" until we have the final "mounted" routes. But according to RFC path '' == '/'.
Also image MyCatalogServiceProvider comes from a library. Then it would decide if it's /catalog or /catalog/.

@nickshanks
Copy link

Hi. I've just ran into bug #4322 as well. I have a bundle prefix of /foo and wanted to specify a resource in the bundle's routing file at /foo, not at /foo/, but was unable to. I suggest also supporting ~ (YAML's null value) as an equivalent to "" for the path. I am not keen on the trailing_slash suggestions posted—just allow an empty string to be concatenated to the prefix, to refer to a route at the prefix path.

@Tobion You say " But according to RFC path '' == '/'. " — yes, but that only applies to the root of a server. We're talking about deeper down in the hierarchy here. "/foo" + "" == "/foo"

Here's what I wrote before finding this bug:

in ./app/config/routing.yml:

my_bundle:
    resource: "@MyBundle/Resources/routing.yml"
    prefix: /path/to/where/bundle/starts

in ./src/MyBundle/Resources/routing.yml:

my_bundle_index:
    path: ~

Expected path of "my_bundle_index" would be /path/to/where/bundle/starts

@Tobion
Copy link
Contributor Author

Tobion commented Jun 30, 2015

The thing is you know that you import my_bundle_index under a prefix, which is why you can use a null path. But for bundles providing routes it doesn't make sense to have a null path since they cannot assume how it's going to be used. So we have to treat each routing file as it could also be used standalone, which is why I said '' == '/'. Each route maps to the Symfony\Component\Routing\Route class (which logically already has the logic '' == '/'. When prefixed it will just add the prefix to the path. So at this point it's too late to know whether the original path was meant to be null. So this would require a complete different approach.

@nickshanks
Copy link

Well that's simple to solve logically—in fact I can think of two obvious solutions. 1) Any bundle that has a empty route path, throws an exception when loaded if it's not loaded under a prefix. It's a condition of use of such bundles that they be imported under a prefix. Or 2) once all paths are loaded, any full path which resolves to an empty string is upgraded to "/" and treated as if it were specified as such in config. i.e. You defer the '' == '/' logic to after prefixes have been applied.

@Tobion
Copy link
Contributor Author

Tobion commented Jun 30, 2015

Again, this still doesn't provide a way of the route user to configure trailing slashes. To me, the one that uses the routes should be in control of how the routes look like (trailing slash or not). Thus my proposal. But your proposal only gives control for the one who defined the routes.

@nickshanks
Copy link

I'm not quite sure what you mean by "the one who uses the routes" and "the one who defines the routes". At the moment, we can't choose "no slash" because the root of a bundle always has a slash added to it. Instead, we are forced to put some of the prefix in the bundle, which puts some of the path in a place it doesn't belong:

my_bundle:
    resource: "@MyBundle/Resources/routing.yml"
    prefix: /path/to/where/bundle
my_bundle_index:
    path: /starts
my_bundle_page1:
    path: /starts/page
my_bundle_page2:
    path: /starts/another-page

@sebastianblum
Copy link

In the discussion, we have the pro and cons with "the one who uses the routes" and "the one who defines the routes".

The main problem is in my opinion, that the path is defined like this

index:
    path: /

Path is:
/ without prefix
/prefix/ with prefix

If we define instead of path: / something like path: ~ or a different character

index:
    path: ~

We can define:

  • path: ~ is the same as / without a prefix
  • path: ~ is like an empty string with a prefix

Path would now be:
/ without prefix
/prefix with prefix

Because we introduce a new character for the index route, we have not problems with backwards compatibility.

maybe this could be a solution for the trailing slash. What you think about that @Tobion

@Ronnie-J
Copy link

So where are we on this issue?

@NothingWeAre
Copy link
Contributor

I agree with @sebastianblum solution.
And this issue is rather unnerving when i'm trying to create REST API.
Fo example dealing with list retrival
/orders rediretcs to /orders/
so i have to define routes fo orders retrival in root route file to avoid such behavior.

@Finesse
Copy link

Finesse commented Feb 5, 2016

Symfony Routing is so smart that automatically changes such paths as dir to /dir. Internet users are used to treat URL with and without ending slash as the same URL. So why not to teach Routing automatically change /dir/ to /dir or treat them as the same paths?

@giosh94mhz
Copy link
Contributor

@FinesseRus because they are not the same path. The fact that most webserver threated with-/ and without-/ is the source of the problem. Currently, Symfony is smart enough that if you use a prefix it imply that a prefix act like a webserver directory, but the whole discussion here is to provide a way for implementing the "prefix is not a directory". (if I understand correctly, of course)

@ThisIsAreku
Copy link

It's been more than 2 years now. I'm in favor of @sebastianblum solution, it seems more natural in the context of REST.

@yellow1912
Copy link

Where are we on this issue? I have the same problem with the traling slash when using prefix.

@mdeboer
Copy link
Contributor

mdeboer commented Aug 24, 2017

With @sebastianblum on this one, nice and clean solution. Currently using the following workaround but his solution would make it much cleaner. Fortunately when working building a REST API you only need this for your index path(s).

my_route:
  path: /{ts}
  defaults: { _controller: AppBundle:Default:index, ts: null }
  requirements:
    ts: /?

@tomsykes
Copy link

Is this not a question of the order in which things are done?

I understand that we can't have an empty route - and so an empty route is normalised to "/" in order to prevent that.

At the moment, a route is normalised before the prefix is added. So an empty route becomes "/" and adding a prefix makes it "/prefix/"

Shouldn't the normalisation be done after any prefixing, so that "/prefix" can be left as is, but if a route is still empty even after a prefix has been added, then it can be normalised to "/"?

@andyexeter
Copy link
Contributor

Any movement on this? It's been three years since this thread was created and the issue still remains.

@mdeboer
Copy link
Contributor

mdeboer commented Nov 27, 2017

I hope this gets fixed in 3.4/4.0 otherwise I'll need to find some time and look into it myself. My workaround works but it's ugly as hell.

@yellow1912
Copy link

I sure hope it gets fixed, there are many cases we don't want Symfony to automatically apply the slash

For example, I have something like this

_frontend:
    prefix: /{_locale}
    resource: frontend.yml
    defaults:
        _locale: '%kernel.default_locale%'
    requirements:
        _locale: ^[a-z]{2}([-_]{1}[A-z]+)?$

Then my homepage route is:

core.frontend.home.index:
    path: /.{_format}
    methods: [GET]
    defaults:
        _controller: theme.frontend.controller.page:showAction
        _format: html
    requirements:
        _format: (html|partial|json)

I want the localized url to be site.com/en, not site.com/en/, but because Symfony appends the slash it is forced to site.com/en/

@linaori
Copy link
Contributor

linaori commented Feb 3, 2018

What would be the reason to not want with a trailing slash in that scenario? If anything, with trailing slash, both urls will work (with a 301 to with slash).

@mdeboer
Copy link
Contributor

mdeboer commented Feb 3, 2018

@iltar because without a slash currently no 301 redirect is being made, instead a 404 is returned. See my comment above to see what "hack" is needed to make things work as they're supposed to.

@linaori
Copy link
Contributor

linaori commented Feb 23, 2018

@dejayc this isn't really adding anything useful to the issue, especially not considering you've not been using Symfony for 3 years. Mind staying on topic please? Thanks!

@nicolas-grekas
Copy link
Member

@dejayc we where waiting for a PR of yours, what did you do for 3 years? Since it's now pretty obvious we shouldn't have, here is one: #26284, implementing @sebastianblum's proposal (sorry couldn't resist :) )

@nicolas-grekas
Copy link
Member

@dejayc if you don't mind and don't care, please unsubscribe from the issue.

@01e9
Copy link

01e9 commented Feb 25, 2018

Similar issue with @Route

/admin/actions/ <---
/admin/actions/add
/admin/actions/{id}

Fortunately @Route allows empty path="" so the fix is

/**
 * @Route(path="/admin/actions")
 */
class AdminActionsController extends Controller
{
    /**
     * @Route(path="", name="admin_actions")
     */
    public function indexAction(Request $request);

    /**
     * @Route(path="/add", name="admin_action_add")
     */
    public function addAction(Request $request);

    /**
     * @Route(path="/{id}", name="admin_action_edit")
     */
    public function editAction(Request $request, string $id);
}

Symfony 4.0

@fabpot fabpot closed this as completed Mar 22, 2018
fabpot added a commit that referenced this issue Mar 22, 2018
…las-grekas)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[Routing] allow no-slash root on imported routes

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #12141
| License       | MIT
| Doc PR        | -

With this change, a collection is imported, its root can have no slash appended. e.g.:

```yaml
import:
    resource: ...
    trailing_slash_on_root: false
```

Works also for XML and PHP-DSL.

Commits
-------

5a98515 [Routing] allow no-slash root on imported routes
@mdeboer
Copy link
Contributor

mdeboer commented Mar 22, 2018

@fabpot Thank you so much, that's amazing. Can't wait for 4.1! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests