4
votes

I have little problem with my "Admin" area registration, which are stored in AdminAreaRegistration.cs inside "Admin" area folder. My routes look like this:

        context.MapRoute(
            "Admin_News",
            "Admin/News",
            new { controller = "News", action = "Index" }
        );

        context.MapRoute(
            "Admin_Pages",
            "Admin/Pages",
            new { controller = "Pages", action = "Index" }
        );

        context.MapRoute(
            "Admin_Galleries",
            "Admin/Galleries",
            new { controller = "Galleries", action = "Index" }
        );

        context.MapRoute(
            "Admin_Categories",
            "Admin/Categories",
            new { controller = "Categories", action = "Index" }
        );

        context.MapRoute(
            "Admin_Products",
            "Admin/Products",
            new { controller = "Products", action = "Index" }
        );

        context.MapRoute(
            "Admin_Files",
            "Admin/Files",
            new { controller = "Files", action = "Index" }
        );

        context.MapRoute(
            "Admin_Orders",
            "Admin/Orders",
            new { controller = "Orders", action = "Index" }
        );

        context.MapRoute(
            "Admin_Codes",
            "Admin/Codes",
            new { controller = "Codes", action = "Index" }
        );

        context.MapRoute(
            "Admin_Login",
            "Admin/Login",
            new { controller = "Login", action = "Index" }
        );

        context.MapRoute(
            "Admin_Panel",
            "Admin",
            new { controller = "Index", action = "Index" }
        );

        context.MapRoute(
            "Admin_Default",
            "Admin/{controller}/{action}/{param}",
            new { param = UrlParameter.Optional }
        );

Problem is: When I get login on post I use

return Redirect("~/Admin");

Everything is fine, until I want to click on link (which is generating well)

<li><a href="@Url.RouteUrl("Admin_Categories")">Categories</a></li> ==
<li><a href="~/Admin/Categories">Categories</a></li>

Then I'm suppose to be redirected to Index action of Categories controller, but instead I'm redirected to Index action of Login controller. Any ideas why is it happening like this?

Best regards!

EDIT 1:

Well, I've changed Admin_Categories route to this one:

context.MapRoute(
    "Admin_Categories",
    "Admin/Categories/{action}",
     new { controller = "Categories" }
);

It seems to work, but now url is generating with action name, which I wouldn't be like this in case of Index action:

<li><a href="@Url.RouteUrl("Admin_Categories")">Categories</a></li> ==
<li><a href="~/Admin/Categories/Index">Categories</a></li>

How can I remove action name from url?

EDIT 2

Okay, so somehow my browser cached Categories controller Index action view as Login controller Index action view. Problem was solved for a while after clearing browser cache. I used below cache attribute for all views (just used VarByParam for views that need parameters):

[OutputCache(CacheProfile = "OneDayCache")]

But even clearing cache doesn't repair the problem. Still I'm getting redirected to Login controller Index action... Any ideas?

EDIT 3

I used those AreaRoutes:

context.MapRoute(
    "Admin_Categories",
    "Admin/Categories",
    new { controller = "Categories", action = "Index", param = UrlParameter.Optional },
    new[] { "AMBIT_CMS_MVC.Areas.Admin.Controllers" }
);

context.MapRoute(
    "Admin_Default",
    "Admin/{controller}/{action}/{param}",
    new { controller = "Index", action = "Index", param = UrlParameter.Optional },
    new[] { "AMBIT_CMS_MVC.Areas.Admin.Controllers" }
    );

I used this to create route url:

@Url.RouteUrl("Admin_Categories")

But still not working as it should. Admin routes are registered in Global.asax and there isn't any route that can override those...

EDIT 4

I've found that my problem is caching. Chrome somehow cache my /Admin/Categories location as /Admin/Login, so when i hit url /Admin/Categories cache is redirecting it to /Admin/Login.

I use OutputCache attribute, but it seems not work properly.

[OutputCache(CacheProfile = "OneDayCache", VaryByCustom = "Url")]
public ActionResult Index(){...}

VaryByCustom set in Global.asax:

public override string GetVaryByCustomString(HttpContext context, string arg)
    {
        if (arg.Equals("Url"))
        {
            string url = !string.IsNullOrWhiteSpace(context.Request.Url.AbsoluteUri) ? context.Request.Url.AbsoluteUri : string.Empty;
            return "Url=" + url;
        }
        return base.GetVaryByCustomString(context, arg);
    }

http://localhost:50945/Admin/Categories
HTTP/1.1 302 Found
Cache-Control: private, max-age=86400
Content-Type: text/html; charset=utf-8
Expires: Thu, 09 Feb 2017 20:24:19 GMT
Last-Modified: Wed, 08 Feb 2017 20:24:19 GMT
Location: /Admin/Login
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?TTpcUHJvamVrdHlcQU1CSVQtQ01TLU1WQ1xBTUJJVCBDTVMgTVZDXEFkbWluXENhdGVnb3JpZXM=?=
X-Powered-By: ASP.NET
Date: Wed, 08 Feb 2017 20:24:19 GMT
Content-Length: 439

Maybe this VaryByCustom isn't working. Any ideas how to fix it?

4
The new { controller = "Categories" } specifies the defaults, but you have no default for {action}, so add action = "Index" to the last MapRoute statement defaults.Brian Mains
In this case, routing is redirecting me to Login controller Index action. But url of link is ~/Admin/Categories.Ashiv3r
Most likely, some route that is configured before the routes you have posted here that exists in your routing configuration is matching on /Admin/Categories. But nobody can tell you what is wrong unless you post your entire route configuration here, including other areas, attribute routes, and routes from your RouteConfig.cs file. Be sure to include the lines where you make the calls to AreaRegistration.RegisterAllAreas() and RouteConfig.RegisterRoutes() (specifically what order they are called in).NightOwl888

4 Answers

5
votes

To your first edit:

The third parameter registers the defaults. You are missing the default action.

Change your route to this, and it will work:

context.MapRoute(
    "Admin_Categories",
    "Admin/Categories/{action}",
     new { controller = "Categories", action = "Index" }
);

The better solution:

Why not simple remove everthing but this:

context.MapRoute(
    "Admin_Default",
    "Admin/{controller}/{action}/{param}",
    new { param = UrlParameter.Optional }
);

This means for example that the request /Admin/Foo/Bar get resolved like this:

  • Find FooController in Admin-Area
  • Call Bar-Method on it.

Then simple use this for generating URL's

@Url.Action("ActionName", "ControllerName", new { Area = "AreaName" })

You maybe want to have this:

context.MapRoute(
    "Admin_Default",
    "Admin/{controller}/{action}/{param}",
    new { controller = "Index", action = "Index", id = UrlParameter.Optional }
);

If you call

@Url.Action("Index", "ControllerName", new { Area = "AreaName" })

The generated Url would be /AreaName/ControllerName instead of /AreaName/ControllerName/Index.

If you call

@Url.Action("Index", "Index", new { Area = "AreaName" })

The generated Url would be /AreaName instead of /AreaName/Index/Index.

All together you should use this AreaRegistration:

public class AdminAreaRegistration : AreaRegistration 
{
    public override string AreaName 
    {
        get 
        {
            return "Admin";
        }
    }

    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            "Admin_default",
            "Admin/{controller}/{action}/{id}",
            new { controller = "Index", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Only create a new Route if you realy need it. Then don't forget to set the defaults in the third parameter of MapRoute.

Also note, it is enogh to write the action, if your already in a view from the same controller:

@Url.Action("SomeAction")

And if you are in a view from the same area, but want to call another controller, you could write this:

@Url.Action("SomeAction", "SomeController")
0
votes

Have you tried decorating your controller class with route attributes?

[Area("Admin")]
[Route("Categories")]
public class CategoriesController : Controller

This worked for me on a similar issue I was having, I was using ASPNET Core though, what version of MVC are you using?

0
votes

Your routing code looks fine to me. Are you sure that in login method you are authenticated properly? Just try removing [Authorize] tag from your "Categories" controller and see if authorization is the issue.

0
votes

This is a basic implementation of the Area feature in MVC:

In your AdminAreaRegistration.cs:

context.MapRoute(
    "admin_default",
    "admin/{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional }
);

Decorate your Controllers with:

[Area("Admin")]
public class MyController : Controller

Also do not forget to add the following line of code in global.asax, on you Application_Start() method (on top of everything):

AreaRegistration.RegisterAllAreas();

In view use:

Url.Action("Index", "Categories", new { Area = "Admin" })

This way should work just fine. Tested!