Possible bug, but mostly in need of clarification for the docs. https://github.com/aspnet/Docs/issues/6173#issuecomment-386825256
App sets up a page route with an optional route param:
options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");
Works when the param is present: http://localhost:5000/TheContactPage/SomeValue.
Doesn't work when the param isn't present (in spite of designation as optional): http://localhost:5000/TheContactPage. The app loads the app's Index page (i.e., no route matches).
Side-note: Upon rendering, the Contact page link in the Layout page points to /TheContactPage (<a href="/TheContactPage">Contact</a>) (good); but since that fails to resolve to the Contact page, it breaks the link.
The following is not a valid workaround ...
options.Conventions.AddPageRoute("/Contact", "TheContactPage");
options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text}");
The behavior doesn't change; and to make matters worse, the layout link to Contact (<a asp-page="/Contact">Contact</a>) isn't rendered to /TheContactPage ... it renders without a destination (<a href="">Contact</a>).
Microsoft.AspNetCore.Mvc or Microsoft.AspNetCore.App or Microsoft.AspNetCore.All:<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.3" />
By-design? ... or bug? If by-design, I'll document that optional route params don't work with AddPageRoute.
App sets up a page route with an optional route param as described above ...
options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");
After /TheContactPage is requested, it fouls other Layout page links in the topic's sample app.
They go from (first rendered) ...
<ul class="dropdown-menu multi-level">
<li><a href="/OtherPages/Page1">Page 1</a></li>
<li><a href="/OtherPages/Page2">Page 2</a></li>
<li><a href="/OtherPages/Page3">Page 3</a></li>
</ul>
... to (rendered after /TheContactPage is requested) ...
<ul class="dropdown-menu multi-level">
<li><a href="/OtherPages/Page1/TheContactPage">Page 1</a></li>
<li><a href="/OtherPages/Page2/TheContactPage">Page 2</a></li>
<li><a href="/OtherPages/Page3/TheContactPage">Page 3</a></li>
</ul>
Thanks @guardrex.
@rynowak, can you please answer this? Thanks!
This all sounds really bizzare to me. I'll have to try and reproduce this to see what's going on. It's definitely intended that you can use optional parameters in pages routing.
@guardrex are there other pages at play here? Is there a page defined with the name TheContactPage.cshtml?
Let me check and confirm that the topic sample is the exact repro for these behaviors. I think it is. brb
Ya, if you have a link to an existing repro I'll just use that to investigate
@rynowak Yes, all of the behaviors described in the OP are evidenced by the topic's sample app ...
https://github.com/aspnet/Docs/tree/master/aspnetcore/razor-pages/razor-pages-conventions/sample/
Make requests to ...
/TheContactPage and /TheContactPage/somevalue ... shows the 1st problem of optional param not working for the first request but with a param it works.
Then, make a request for /TheContactPage (the app just loads the Index page) and pop open the "Other Pages" menu item and check the links there ... they're fouled.
Alright dope, I'll take a look 馃憤
Wow, that's a big repo 馃槅 - does it help if you comment out https://github.com/aspnet/Docs/blob/master/aspnetcore/razor-pages/razor-pages-conventions/sample/Conventions/GlobalTemplatePageRouteModelConvention.cs ?
I'm still downloading all of the code so I haven't been able to test it yet, but I suspect that convention is making it so that /Index will have a route template with order -1 of /{globalTemplate?} - that would match /TheContactPage -- and since the order is -1 it will run before the route you added for /TheContextPage/{text?}.
still downloading all of the code
You hit something we hear about constantly from the community. Docs engineers are working on it ... something that will allow individual samples to be downloaded. https://github.com/aspnet/Docs/issues/6991#issuecomment-398104010
Yes, indeed on your early guess. The sample app is a bear trying to explain all of the scenarios. No, I didn't try commenting out the other scenarios. I'll try that now and let you know.
YES! You are correct. Let me take over ... I can work off your remark. That bad interaction of scenarios in the sample app is what's fouling things.
There was a third issue raised by @user135711 ... but I'll resolve this first/second problem then see if the third problem is related. If not, I'll open a new issue.
Thanks for your help.
Alright, so the good news is that I'm right 馃槅
So if you turn the logs up to debug and visit: /TheContactPage - you'll see that the problem is what I suspected.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET https://localhost:5001/TheContactPage
dbug: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
Wildcard detected, all requests with hosts will be allowed.
dbug: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[4]
The request path /TheContactPage does not match a supported file type
dbug: Microsoft.AspNetCore.Routing.Tree.TreeRouter[1]
Request successfully matched the route with name '(null)' and template '{globalTemplate?}'.
The route added by GlobalTemplatePageRouteModelConvention is what's being matched.
For the issues with link generation, it's the same thing. You're seeing the route added with order -1 do the link generation. The fact the the URLS change from /OtherPages/Page1 to /OtherPages/Page1/TheContact is a little suprising at first, but it has a sensible explanation. This is the ambient route value for globalTemplate echoed back. This is expected in 2.1 and will be fixed in 2.2
For instance if you go to /Foo then the index page gets rendered, after matching the /{globalTemplate?} route, and then the URLs look like:

The lesson here is to really be careful with route templates of varying lengths. If you have a route template that's very generic (like appending an optional path segment) then you want to make that have the same order as everything else.
The route system already does work to figure out the 'most desirable' sequence of processing templates, Order should be used as an override only when necessary.
Cool! Thanks @guardrex