JianYA
JianYA

Reputation: 3024

ASP.NET Core 2.1 Areas routing not working

I have this folder structure for my new Area

enter image description here

This is how I set it up in my startup:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");

    routes.MapRoute(
      name: "areas",
      template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
    );
});

This is how I created my basecontroller

namespace App.Areas.Applications.Controllers
{
    [Area("Applications")]
    [Authorize]
    public abstract class ApplicationsBaseController : Controller
    {

    }
}

My ApplicationsController then inherits the BaseController

However, when I set a link like this

<li class="nav-item"><a asp-area="Applications" asp-controller="Applications" asp-action="Index" class="nav-link">Applications</a></li>

This is the link that shows up in my url https://localhost:44338/Applications?area=Applications and I get a page cannot be found.

What did I miss when setting up my Area?

EDIT:

When I add [Route("Applications/[controller]")] after my [Area("Applications")], I get this error

An unhandled exception occurred while processing the request. AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:

App.Areas.Applications.Controllers.ApplicationsController.Index (App) App.Areas.Applications.Controllers.ApplicationsController.Create (App) App.Areas.Applications.Controllers.ApplicationsController.NewRole (App)

Upvotes: 4

Views: 7794

Answers (5)

Zanyar Jalal
Zanyar Jalal

Reputation: 1874

you can use simple solation

Extension

public static class AreaExtension
{
    public static string AreaUrl(this IHtmlHelper helper, string action,  params string[] parameters)
    {
        var viewContext = helper.ViewContext.RouteData.Values;

        string controller = (string)viewContext["controller"],
         area = (string)viewContext["area"];
        return GenerateUrl(action, controller, area, parameters);
    }

    public static string AreaUrl(this IHtmlHelper helper, string action, string controller, params string[] parameters)
    {
        var viewContext = helper.ViewContext.RouteData.Values;

        string area = (string)viewContext["area"];
        return GenerateUrl(action, controller, area, parameters);
    }

    public static string AreaUrl(this IHtmlHelper helper, string action, string controller, string area, params string[] parameters)
        => GenerateUrl(action, controller, area, parameters);

    private static string GenerateUrl(string action, string controller, string area, params string[] parameters)
    {
        if (action == null)
            throw new ArgumentNullException(nameof(controller));
        if (controller == null)
            throw new ArgumentNullException(nameof(action));

        string urlParams = string.Empty;

        if (parameters != null && parameters.Length > 0)
            urlParams = "?" + string.Join("&", parameters);

        return "/" + string.Join("/", area, controller, action) + urlParams;
    }
}

Usage

Incluse namespase AreaExtension in _ViewImports.cshtml

<li class="nav-item"><a href="@Html.AreaUrl("Index", "Applications","Applications")" class="nav-link">Applications</a></li>

This AreaExtension have 3 overload

Upvotes: 0

psavov
psavov

Reputation: 41

There is another option which does bring some clarification in terms of meta data of the code:

Using MapAreaRoute extension method in Namespace: Microsoft.AspNetCore.Builder

Here is an example:

          routes.MapAreaRoute(
                name: "AdminArea",
                areaName: "Admin",
                template: Admin/{controller=Home}/{action=Index}/{id?}");

It is available from .NET Core 1.0

Microsoft documentation

Upvotes: 0

JianYA
JianYA

Reputation: 3024

I realized what the issue was. In each controller, I needed to declare [Area="AreaName"] in the top before anything else so that the routing worked.

Thank you everyone for your help.

Upvotes: 0

Edward
Edward

Reputation: 29986

IMO, you should create Controller folder to the specific views. Otherwise, it will fail when there are multiple controllers in the Applications area.

Anyway, for returning the views just in Views Folder, try to configure the AreaViewLocationFormats to specify the views search location.

        public void ConfigureServices(IServiceCollection services)
    {
        //rest services
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();
        services.Configure<RazorViewEngineOptions>(o =>
        {
            o.AreaViewLocationFormats.Add("/Areas/{2}/{0}" + RazorViewEngine.ViewExtension);
        });
    }

Upvotes: 0

rykamol
rykamol

Reputation: 1147

Put it before the default route... like this

app.UseMvc(routes =>
{
 routes.MapRoute(
  name: "areas",
  template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);

routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id?}");
});

Upvotes: 12

Related Questions