Problem/Motivation
If you link to a non-existent route from your code, it breaks the whole webpage on which the link is displayed. A Symfony\Component\Routing\Exception\RouteNotFoundException exception is thrown, and an "unexpected error" page is displayed to users.
This was probably done based on the expectation that it's due to broken code, but it's a pretty harsh solution considering the following scenarios:
- If module A links to a route provided by module B, and the two modules are usually used together (but not always), then the developer probably won't notice the problem while writing their code. But as soon as someone uses module A on a site where module B isn't installed, it will break their site. This is not a made-up scenario; it actually happened in core: #2473105: Update hook_help texts that link to modules that can be uninstalled
- If module A links to a route provided by module B, and module B's code is updated to no longer define that route, then when a site updates module B to the newest version, module A will break.
Proposed resolution
I would expect this to behave like links in Drupal 7 and earlier do. If code tries to link to a page that doesn't exist, the link itself should render fine. When the user clicks on the link, they should get a 404.
Exactly how to achieve that is questionable though, since in the case of routes it is not clear what URL would display in the browser (the system doesn't have an actual URL to use)...
Comment | File | Size | Author |
---|---|---|---|
#25 | 2498675-404_for_missing_routes.patch | 1.36 KB | MaskOta |
Comments
Comment #1
webchickThis feels pretty major to me.
Comment #2
cilefen CreditAttribution: cilefen commentedOn catching RouteNotFoundException in the proper place(s), perhaps produce a '#' and carry on.
Comment #3
dawehnerI agree we should not blow up, but on the other hand it would be interesting to know for module developers that such a case exist, as they should better introduce a
module_exists()
or similar. Does logging makes sense in that case?One major difference to D6/D7 is that we no longer have the path, so as @cilefen wrote we can maybe like to
<none>
.Comment #4
opratr CreditAttribution: opratr as a volunteer commentedComment #5
xjmI can see the case for handling a missing route more gracefully, though technically a missing route is broken code.
I don't think linking to
#
is the correct way to handle this. In addition to the point @dawehner makes about logging, the magic behavior / silent non-failure seems incorrect. If the code links to a resource that doesn't exist, it seems to me like that link should be to a 404. This 404 request could log the fact that the route was missing and/or present this information on the 404 page.Furthermore, something that appears to be a link but basically does nothing seems like a usability and accessibility problem.
Comment #6
xjmNot novice at this point since there is not a consensus. :)
Comment #7
cilefen CreditAttribution: cilefen commentedI like #5. +1 for a 404 - that is how the web is supposed to work.
Comment #8
cilefen CreditAttribution: cilefen commentedTo accomplish #5, we would have to create some kind of default 404 route in the system, and modify UrlGenerator::getRoute() to return it.
Comment #9
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedWell that brings it back to my question in the issue summary:
What do we link to in order to generate a 404? I mean there are lots of possibilities -
/fddsafaffdfdasfadsfdas
would probably work 99.9999% of the time :) But since we don't know what the code is trying to link to, it's just not obvious what URL the user should see in their browser in this case.Anyone have any ideas? Do we (or should we) have some kind of dedicated 404-only route for that purpose?
Comment #10
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedCross-post :)
Comment #11
cilefen CreditAttribution: cilefen commentedCould we not pass the string name of the bogus route as a parameter?
Comment #12
cilefen CreditAttribution: cilefen commentedIn other words, the URL would be /some/predetermined/404/path/bad_route_name.
Comment #13
tim.plunkettSeems similar but unrelated to #2532490: Unrouted URLs break toolbar but are hidden by caching
Comment #14
Crell CreditAttribution: Crell at Palantir.net commentedThere's more going on there, but #2532490: Unrouted URLs break toolbar but are hidden by caching is a similar problem space. (Different exception, similar underlying cause: Insufficient of handling of invalid links.) Cross-linking.
Comment #15
dawehnerYeah the question is whether we want to switch from exceptions to errors internally and otherwise return safe values like an empty string
Comment #16
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedI just ran into the same problem with permissions not realizing that 'access content' is now only provided when 'node' module is installed, which is not in 'testing' profile.
Therefore, I think that problem is pre-existing (In other spaces), we just have to realize that D8 is a lot more strict about links and urls.
Maybe a good idea would be to namespace (further new routes) as a convention with the module name?
Then it is pretty clear it is not a generic route ...
Just some thoughts ...
--
Question: I assume user input urls and links are not affected as they go via a different code path, right?
Comment #17
tim.plunkettWe shipped with this, and I haven't heard anyone complain since.
I personally think we should mark it works as designed. If you don't own the route name you're linking to, your code should do whatever is necessary to not break if that route goes away (moduleExists, try/catch, ...)
Comment #18
Crell CreditAttribution: Crell at Palantir.net commentedIt should at least fail gracefully. Even if that's a better description in a global exception catch so you get a useful error message.
Comment #19
dawehnerThis is what is happening:
I'm not sure whether this is gracefully or not.
Comment #22
tim.plunkettGraceful enough for me
Comment #23
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI don't think that's graceful - fixing that was actually the entire point of this issue.
A broken link should really just be a broken link; it should not result in the entire page that the link was on being replaced with an error message and thereby preventing the user from seeing the rest of the page's content.
I noticed recently that if you are on a 404 page and call
Url::fromRoute('<current>')
, the generated URL points to something like "system/404". It is not obviously the correct behavior in that case, but I think it is correct behavior in the case of this issue - basically it is the answer to the question I asked above about whether there should be a dedicated 404-only route for this purpose.So I propose that this issue be fixed by making
Url::fromRoute('some-path-that-does-not-exist')
point to the system/404 page. This could be accompanied by a trigger_error() or similar, so that developers can be informed that there is a broken link in the code.Comment #24
tim.plunkettComment #25
MaskOta CreditAttribution: MaskOta commentedLike so?
Comment #26
dawehnerI'm wondering whether we could do this kind of error handling maybe further out in the layers ... Errors are something you an't catch in PHP 5.5/PHP 5.6. Maybe the URL generator is a better place?