How to use DynamicRouteValueTransformer to switch between 2 controllers and actions

208 views Asked by At

I have 2 similar controllers in different areas that I am trying to route depending on the user type. For example the two controllers have the index action that is supposed to show the home page;

WebApp.Areas.Candidate.Controllers.SocialController.Index
WebApp.Areas.Agent.Controllers.SocialController.Index

Currently when I add the HttpGet("") attribute to both actions I get the exception; AmbiguousMatchException: The request matched multiple endpoints. Matches:

So I have removed the attribute and created a DynamicRouteValueTransformer to try and dynamically change which controller and action the "/" endpoint should use but the transformer is not using my changes. The transformer looks like this;

public class UserModeTransformer : DynamicRouteValueTransformer
{
    public readonly ApplicationDbContext _context;

    public UserModeTransformer(ApplicationDbContext context)
    {
        _context = context;
    }

    public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
    {
        values = new RouteValueDictionary();
        values["controller"] = "SocialController";
        values["action"] = "Index";
        return values;
    }
    public override async ValueTask<IReadOnlyList<Endpoint>> FilterAsync(HttpContext httpContext, RouteValueDictionary values, IReadOnlyList<Endpoint> endpoints)
    {
        return endpoints;
    }
}

For now I just set the RouteValueDictionary values to see whether it works but no matter which controller I change it to the DynamicRouteValueTransformer does not work.

I have tried to look for documentations about DynamicRouteValueTransformer but I can't find any. I am using ASP.NET Core 7.

Update Even having just this transform on the route without any user check returns 404;

public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            values = new RouteValueDictionary();
            values["area"] = "Candidate";
            values["controller"] = "SocialController";
            values["action"] = "Index"; 
            return values;
        }
1

There are 1 answers

14
Xinran Shen On

I haven't tried this before, But after searching some related information, I create a working demo. I am not sure if it is what you want, Hope it can give you some help.

There are two areas in my project:

IdentityTest\Areas\Authenticate\Views\Social\Index.cshtml

and

IdentityTest\Areas\Candidate\Views\Social\Index.cshtml

Because I don't know what's the user type in your application, So here is my logic: When user don't login ,when he type '/' in url, it will route to 'Home/Index' normally. After user log in, when he type '/' in url, project will route to area depend on user's name, If username is [email protected] it will route to Authenticate\Social\Index, the other will route to Candidate\Social\Index

public class UserModeTransformer : DynamicRouteValueTransformer
    {
        public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            var username = httpContext.User.Identity.Name;
            values = new RouteValueDictionary();
            if (username != null)
            {
                values["area"] = username == "[email protected]" ? "Authenticate" : "Candidate";
                values["controller"] = "Social";
                values["action"] = "Index";

                return values;
            }
            else
            {
                values["controller"] = "Home";
                values["action"] = "Index";
                return values;
            }
        }
    }

register and use it in program.cs

            builder.Services.AddScoped<UserModeTransformer>();
            //............

            app.MapDynamicControllerRoute<UserModeTransformer>("");

            app.MapControllerRoute(
            name: "MyArea",
            pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
            

            app.MapControllerRoute(
               name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");
            app.MapRazorPages();

            app.Run();

enter image description here