Changing the default file
An interesting question came up on GitHub about changing the default file in a folder. Currently it is a file named "Index.cshtml", and this is enforced by the default convention. A lot of applications have "feature folders", folders named after the area of the business that their content is concerned with, such as Contacts, Companies, Administration, Products, Orders etc. Something perhaps like this:
I've actually got an application with 50 or so of these feature folders. In each one there is usually an Index.cshtml file, an Edit.cshtml file, a Create.cshtml file and so on. It is not uncommon for me to have multiple Index.cshtml files open at the same time in Visual Studio, and just like JohnGoldsmith, the author of the issue that was logged, I find navigating from one to another can be a pain, especially if things are playing up and the tooltips take a while to appear when you hover over the file name in the list:
So how about this for a solution? You name each file according to its action and its feature, so the Edit.cshtml file in the Contact folder becomes ContactEdit.cshtml. And you get ContactIndex.cshtml, CompanyIndex.cshtml etc. It is immediately obvious what each file is responsible for. Except that now there are no default documents and users have to get used to a new URL scheme. You can either go in and apply a route template to each file manually, or you can create a new route convention for all the pages in your application.
IPageRouteModelConvention interface is designed to allow the customisation of the
PageRouteModel, an object that lives in the
Microsoft.AspNetCore.Mvc.ApplicationModels namespace and represents a Razor Page's routing setup. In other words, you can use this component to override the default conventions.
The interface has one member that needs to be implemented:
void Apply(PageRouteModel model). It is in this method that you can access metadata about the current routing set up and modify it as required. The following example solves the problem outlined above so that requests to
/contact go to Contact/ContactIndex.cshtml, those that go to
/contact/edit reach /Contact/ContactEdit.cshtml etc.
In this example, if the template contains a forward slash, it belongs to a file in a folder. The template is divided up into its segments, and if there are two, the template is replaced with one that consists of the folder name followed by the file name with the folder name removed. This means that the original template generated for Contact/ContactEdit.cshtml ("contact/contactedit") becomes "contact/edit". I also replace "Index" with an empty string, and remove any trailing slashes. Therefore the template for Contact/ContactIndex.cshtml becomes "contact".
I have also taken the trouble to enforce a business rule: no nested folders allowed. If the number of segments in the template exceeds two, an exception is raised at application startup. Now, so long as people follow the file naming convention, the new routing convention will be applied. There is no need to remember to specify absolute routes in each page.
The custom convention is registered in
Startup where it is added to the
Another question came up about custom routing, this time on Stackoverflow. The requirement in this instance was to allow users to reach the same page e.g. Contact.cshtml, with a URL in their own language:
/kontakta etc. This can be achieved by adding additional route templates to the Contact page. To illustrate this, here's a simple demo service that gets the translation options for a particular page:
And here is how that service is consumed within a
This time, the
Apply method gets any options for the
PageRouteModel that's currently being processed, and if there are some, it creates additional templates for the page. This is pretty much what the AddPageRoute method does, but this approach is far more scalable. Just imagine having to use
AddPageRoute for 50 pages in 20 languages!
Now the same page can be reached via multiple URLs:
IPageRouteModelConventions are eminently testable. Here's an example test that ensures that the Index page in the first example has its route modified correctly:
Chances are that for most Razor Pages applications, the default routing conventions will work just fine. But if you ever need to customise them, the
IPageRouteModelConvention interface is what you need. It is scalable, testable and pretty easy to use.