Have 2 controllers with the same route in different areas

84 views Asked by At

I have 2 controllers in 2 different areas that will display a different page on the home page depending on the type of user.

The controller actions are at;

MyApp.Areas.Buyer.Controllers.SocialController.Index 
MyApp.Areas.Client.Controllers.SocialController.Index

These will just show some data on the home depending on the user. When the user logs in I save the type of user in a session and check the user type on every request. I managed to create a middleware that checks the user type and changes the target area.

public async Task InvokeAsync(HttpContext httpContext, UserManager<ApplicationUser> userManager)
{
...
    String? k = httpContext.Session.GetString(Contants.ModeSessionKeyName);
    var splittedUrl = url.Split('/');
    if (url=="/")
    {
        switch (k)
        {
            case "_BUYER":
                // httpContext.GetRouteData().Values.Add("area", "My");
                httpContext.GetRouteData().Values["area"] = "BUYER";
                break;
            default:
                httpContext.GetRouteData().Values["area"] = "Client";
                break;

        }
    }
...
    await _next(httpContext);
}

That works fine. Except now there is a conflict between the two routes when I try to run it.

AmbiguousMatchException: The request matched multiple endpoints. Matches:

Is there anyway to prevent this? Can I remove one of the routes at runtime before ASP.NET Core checks and throws the exception?

Here is how the two controllers look like;

[Area("Buyer")]
public class SocialController : Controller
{
    [AllowAnonymous]
    [HttpGet("/")]
    public IActionResult Index()
    {
        ViewData["Title"] = "My Account";
        ViewData["PageHeading"] = "Feeds";
        if (!User.Identity.IsAuthenticated)
        {
            return new RedirectResult("/welcome") { Permanent = false };
        }
        return View();
    }
}

The other controller is exactly the same it just has the attribute [Area("Client")]

1

There are 1 answers

0
Qiang Fu On

The [HttpGet("/")] forced the method real URL to be "/", you shouldn't use it.
You can not have same "real" route. Adding area just make your real URL become Domain/area/controller/action format.

So the real URL of your methods should be domain/buyer/social/index and domian/client/social/index.

Real URL has to be different. But the input URL "/" can be rewrite the area/controller/action in the middleware. Please clarify the difference of real URL and input URL

You can try the following code for a test.

Areas/Buyer/Controllers/SocialController.cs

    [Area("Buyer")]
    public class SocialController : Controller
    {
        [AllowAnonymous]
        public IActionResult Index()
        {
            return View();
        }
    }

Areas/Buyer/Views/Social/index.cshtml

Buyer Page

Areas/Client/Controllers/SocialController.cs

    [Area("Client")]
    public class SocialController : Controller
    {
        [AllowAnonymous]
        public IActionResult Index()
        {
            return View();
        }
    }

Areas/Client/Views/Social/index.cshtml

Client Page

CustomMiddleware.cs

    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
  
            httpContext.GetRouteData().Values["area"]="Buyer";
            httpContext.GetRouteData().Values["controller"] = "social";
            httpContext.GetRouteData().Values["action"] = "index";

            await _next(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.  
    public static class CustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<CustomMiddleware>();
        }
    }

program.cs

app.UseAuthorization();
app.UseCustomMiddleware();
app.MapControllerRoute(
    name: "areaRoute",
    pattern: "{area:exists}/{controller}/{action}"
);
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

Test1
enter image description here
Then change the middleware

        public async Task Invoke(HttpContext httpContext)
        {
  
            httpContext.GetRouteData().Values["area"]="Client";
            httpContext.GetRouteData().Values["controller"] = "social";
            httpContext.GetRouteData().Values["action"] = "index";

            await _next(httpContext);
        }

Test 2
enter image description here

Reference :https://geeksarray.com/blog/how-to-use-areas-in-asp-net-core-mvc