8000 [Router] TraceableUrlMatcher wrong behaviour with trailing slash · Issue #32149 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[Router] TraceableUrlMatcher wrong behaviour with trailing slash #32149
Closed
@xavierleune

Description

@xavierleune

Symfony version(s) affected: 4.3

Description
The TraceableUrlMatcher does not treat the trailing slash the same way the UrlMatcher does.

How to reproduce
On a fresh install of the website skeletton with composer create-project symfony/website-skeleton sf-debug-routes.

Add in your config/routes.yml the following content:

hardlink_with_host_without_trailing_slash:
  path: /search
  controller: App\Controller\DefaultController::index

dynamic_link_with_trailing_slash:
  path: /{search}/
  controller: App\Controller\DefaultController::index
  requirements:
    search: "search"

According to the documentation, this should be treated as one route (/search) - even if I disagree with that behaviour.

And with a dummy DefaultController:

<?php
# src/Controller/DefaultController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    public function index()
    {
        return new Response('ok');
    }
}

Now check the output of the command "router:match" for the 2 paths "/search/" and "/search":

$ php bin/console router:match "/search"                                             
 [OK] Route "hardlink_without_trailing_slash" matches
+--------------+---------------------------------------------------------+
| Property     | Value                                                   |
+--------------+---------------------------------------------------------+
| Route Name   | hardlink_without_trailing_slash                         |
| Path         | /search                                                 |
| Path Regex   | #^/search$#sDu                                          |
| Host         | ANY                                                     |
| Host Regex   |                                                         |
| Scheme       | ANY                                                     |
| Method       | ANY                                                     |
| Requirements | NO CUSTOM                                               |
| Class        | Symfony\Component\Routing\Route                         |
| Defaults     | _controller: App\Controller\DefaultController::index    |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler |
|              | utf8: true                                              |
+--------------+---------------------------------------------------------+

And

$ php bin/console router:match "/search/"
 [OK] Route "dynamic_link_with_trailing_slash" matches
+--------------+---------------------------------------------------------+
| Property     | Value                                                   |
+--------------+---------------------------------------------------------+
| Route Name   | dynamic_link_with_trailing_slash                        |
| Path         | /{search}/                                              |
| Path Regex   | #^/(?P<search>search)/$#sDu                             |
| Host         | ANY                                                     |
| Host Regex   |                                                         |
| Scheme       | ANY                                                     |
| Method       | ANY                                                     |
| Requirements | search: search                                          |
| Class        | Symfony\Component\Routing\Route                         |
| Defaults     | _controller: App\Controller\DefaultController::index    |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler |
|              | utf8: true  
6E40
                                            |
+--------------+---------------------------------------------------------+

So the TraceableUrlMatcher is not redirecting the "/search/" to the "/search", as the route hardlink_without_trailing_slash is defined first.

The web output (with a webserver started with php bin/console server:start) is the following:

Route without the trailing slash is 200:

$ curl -v -o/dev/null http://127.0.0.1:8000/search
> GET /search HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 200 OK
(...)

Route with the trailing slash is 301:

$ curl -v -o/dev/null http://127.0.0.1:8000/search/

> GET /search/ HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Location: http://127.0.0.1:8000/search
(...)

I'm also wondering if that bug can be present elsewhere in the codebase, we had a more complex bug in production, but it was not present in dev or staging envs - not as this one.
The static link was defined with a host match and the dynamic link was not (this dynamic link was imported from a bundle and the "search" requirements was a configuration option of this bundle). The output of router:match in production were similar to the one I posted in this issue and the redirection was not done on any other environments. I wonder if there is an issue with the CompiledUrlMatcher in this case. In production our version of symfony is 4.2.3, I'll make some new tests to try to reproduce this precise use case on a fresh install.

Possible Solution

  • Detect this issue at build and do not allow to define conflicting routes (or add a warning message)
  • Add an option to disable the redirection
  • ... ?

Thanks for your help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0