Mvc: Use Razor Pages in MVC area

Created on 8 Oct 2017  ยท  11Comments  ยท  Source: aspnet/Mvc

I'm trying to mix Razor Pages with MVC and by that I mean running a Razor Pages "app" inside an MVC area. So if I run the app below I would like http://localhost:500 to return the standard MVC HomeController Index action. And if I add the area like this http://localhost:5000/app I would like the Razor Pages app to respond. Is this possible?

  • Areas

    • App



      • _Layout.cshtml


      • _ViewImports.cshtml


      • _ViewStart.cshtml


      • Index.cshtml


      • Index.cshtml.cs



  • Controllers
  • Models
  • Views
  • wwwroot
  • Program.cs
  • Startup.cs
3 - Done enhancement idlib super-triage

Most helpful comment

Spoke to @DamianEdwards and he's recommended EnableAreas that defaults to true false. The feature needs to be specifically enabled.

All 11 comments

You can root the Razor pages in Areas/App folder, but acting it as mini app make it impossible because the Razor Pages has a single root, so this may not applicable in your case

In such scenario may I prefer using OrchardCore, because it build as modules from ground up, so I advice you to have a try

@Eilon @rynowak @mkArtakMSFT @javiercn

We would like to be able to stick all of the Identity related code in a single folder. To do that with Razor Pages we'll need this feature.

Would need to discuss with @DamianEdwards to see if there is moral opposition. It does sound like a reasonable feature to me.

BTW even for Identity couldn't we still put everything in ~/Pages/Identity/*, including CSHTML and CS files? (It's not super clean, but it would work, no?)

I want to do it, we've discussed it as part of the identity in a library work.

Mvc scaffolders use the convention of putting content that belongs to areas - controllers, models and views - in the Areas directory. For the default experience, we can center the feature work around this convention. We'll look for RazorPages in a directory produced by concatenating AreaRootDirectory and RootDirectory

/Areas
    /Products <-- Area Name
        /Controllers
            /HomeController.cs
        /Pages < -- RazorPagesOptions.RootDirectory
            /EditProduct.cshtml
            /EditProduct.cshtml.cs
            /Manage
                /Categories
                    /Index.cshtml
        /Views
            /Home
                /Index.cshtml

We will allow configuring the area root via RazorPagesOptions:

```C#
services.AddMvc()
.AddRazorPages(options =>
{
options.AreasRootDirectory = "/Features";
});

The feature is enabled by default and can be disabled by setting a switch in RazorPagesOptions:
```C#
services.AddMvc()
    .AddRazorPages(options =>
    {
        options.DisableAutoAreasForRazorPages = true;
    });

The result of doing this would be

  • EditProduct.cshtml would be associated with the Products area i.e. with add a RouteValue area=Products for the page.
  • Routes associated would look like

    • EditProduct.cshtml -> Products/EditProducts/{route-suffix}.

    • Categories.cshtml -> Products/Manage/Categories/Index/{route-suffix}, Products/Manage/Categories/{route-suffix}

  • Route generation would have the same semantics as controllers i.e. when routing from a controller or a Razor Page you'd have to specify the area value.
    <a asp-page="EditProduct" asp-area="Products">...</a>
  • Razor Pages do view lookups for two things - when we do Partial \ PartialAsync and for Layout lookups. For RazorPages outside of the area, we traverse up to the /Pages root to look for files. Here's a sample lookup when we a view engine lookup from /Pages/Account/Manage/Index.cshtml

    • /Pages/Account/Manage/{0}.cshtml

    • /Pages/Account/{0}.cshtml

    • /Pages/{0}.cshtml

    • /Views/Shared/{0}.cshtml

  • For pages in an area, we would search up to the RootDirectory in the area:

    • /Areas/Products/Pages/Manage/Categories/{0}.cshtml

    • /Areas/Products/Pages/Manage/{0}.cshtml

    • /Areas/Products/Pages/{0}.cshtml

    • /Views/Shared/{0}.cshtml

In addition, we would expose a property on RazorViewEngineOptions named AreaPagesViewLocationFormats to customize this lookup. Note When AreasRootDirectory is modified, this list must be updated since it assumes Razor Pages in areas are located under a directory named Areas.

Future work:

  • Areas added via convention:
    C# .AddRazorPages(options => { options.Conventions.AddFolderToArea("ManageProducts", "/Features/Products"); });
    This would result all files in the directory hierarchy added to the ManageProducts area. e.g. /Feature/Products/Edit.cshtml will be added to the ManageProducts area with the route /ManageProducts/Edit
  • AreaAttribute in cshtml files โ“

Overall looks great to me. Some questions:

  • It's not clear to me what the default value is for AreasRootDirectory. Is it just Areas? Do we allow customizing that for MVC? If not, why allow it for Razor Pages? If so, should it be the same option for Razor Pages as it is for MVC?
  • When/why would you disable DisableAutoAreasForRazorPages? And why is the word Auto in there?
  • The default value is /Areas. We have RazorViewEngineOptions.AreaViewLocationFormat that allows specifying the directory paths for area view lookups. This defaults to /Areas/{area-name}/Views/. In a similar vein, we'd default to /Areas/{area-name}/Pages for Razor Pages. Like I noted earlier, the user would have to modify both RazorPagesOptions.AreasRootDirectory and RazorViewEngineOptions.AreaPagesViewLocationFormat to get both page and associated view lookups re-rooted.

  • I need a better name for that property - it's called DisableAreas in my PR. The feature is breaking in that the route for a page located under /Areas/MyProduct/Pages/Home.cshtml changes from /Areas/MyProduct/Pages/Home to /MyProduct/Home. The option is designed to back out this behavior change.

Got it, this sounds fine to me. I don't like the name DisableAreas either, so let's get a PM to sign off on the name.

Spoke to @DamianEdwards and he's recommended EnableAreas that defaults to true false. The feature needs to be specifically enabled.

Much better.

Can't even begin to describe what a pain in the arse all of this is.. What is wrong with a relative or non-relative complete path to a file?

After hours (and I mean HOURS) of horsing around trying to get a simple connection to a link the bottom one finally worked.
startup.cs ...
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options =>
options.Conventions.AddPageRoute("/Contacts/Index", "")
)

the project has
Areas
Contacts
Pages
Index.cshtml

(Doesn't work)
(Does Work)

Now here is the question,

What If I wanted to put a folder in between to segregate multiple index pages?
Areas
ContactModel
Pages
ContactPhones
Index.cshtml
Contacts
Index.cshtml

How do I call that? because I could not get it to work in the above.. Thanks, Out of my mind with this and the docs aren't very good at examples outside of the simple

Was this page helpful?
0 / 5 - 0 ratings