I'm using MVC 4 and having problems with the landing page.
I have two kinds of users (let's call the FooUser and BarUser)
Each of the users has it's own landing page:
Foo/Index and Bar/Index
Once user logs in, I can identify whether he is Foo or Bar and redirect him to the relevant page.
But I still have a problem and that is when a user opens the main page. In this case the user doesn't perform a login action (since he is logged in from the previous session) so I can't redirect him to the relevant page.
Is there a way to set conditional defaults? something like:
(Any other ideas are most welcome)
if (IsCurrentUserFooUser()) //Have no idea how to get the current user at this point in the code
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Foo", action = "Index", id = UrlParameter.Optional });
}
else
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Bar", action = "Index", id = UrlParameter.Optional });
}
It might be worth considering if you really need a new controller for different users. Why not just return a different view and do some logic in the controller. This would be my preferred route as it's less overhead then dynamically calculating routes.
Routes are mapped when the application starts so it won't be able to do conditional ones. You could use a dynamic routes which are processed per request so you can do some logic to see if that route matches.
Note: return null at any point in the dynamic route to cancel it and make it invalid for that request.
public class UserRoute: Route
{
public UserRoute()
: base("{controller}/{action}/{id}", new MvcRouteHandler())
{
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var rd = base.GetRouteData(httpContext);
if (rd == null)
{
return null;
}
//You have access to HttpContext here as it's part of the request
//so this should be possible using whatever you need to auth the user.
//I.e session etc.
if (httpContext.Current.Session["someSession"] == "something")
{
rd.Values["controller"] = "Foo"; //Controller for this user
rd.Values["action"] = "Index";
}
else
{
rd.Values["controller"] = "Bar"; //Controller for a different user.
rd.Values["action"] = "Index";
}
rd.Values["id"] = rd.Values["id"]; //Pass the Id that came with the request.
return rd;
}
}
This could then be used like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("UserRoute", new UserRoute());
//Default route for other things
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Related
I am using C# MVC.
I have an action which looks like this.
public class CustomController : Controller
{
public ActionResult CustomPage(int customPageId)
{
var model = new CustomPageViewModel()
{
customPageId = customPageId
};
return View(model);
}
}
I want to be able to hit this action but to use a different route. For example I want Home/Index to actually hit this action, but to report in the URL that its Home/Index.
My custom pages are stored in the database which tell it what route to have, how can I create routes for my pages programatically and have MVC perform the required actions?
As this is a CMS based system, I don't want to have to create a Home/Index controller and action, as the user may choose any route they wish.
Thanks, Tom
FYI: I have sort of figured this out. Unfortunately Routes are defined when the application starts, so I have to force a restart if I want to setup a new route while the app is running...
Here is the code I have used to setup my routes incase it helps anybody else.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var pageList = new PageService().Get();
foreach (var page in pageList)
{
var targetRoute = string.Format("{1}/{2}", page.PageArea, page.PageController, page.PageAction);
if (!string.IsNullOrWhiteSpace(page.PageArea))
targetRoute = string.Format("{0}/{1}/{2}", page.PageArea, page.PageController, page.PageAction);
routes.MapRoute(
string.Format("PageBuilder_{0}", page.PageId),
targetRoute,
new { area = "Builder", controller = "Build", action = "Index", pageId = page.PageId }
);
}
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I'm trying to make a route so I can show the username in the URL like this:
http://localhost1234/john
Here Is my routeconfig:
routes.MapRoute(
name: "users", // Route name
url: "{username}", // URL with parameters
defaults: new { controller = "Home", action = "Index", username = "" } // Parameter defaults
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Here is my HomeController:
public ActionResult Index(string username = "Test")
{
return View();
}
First of all, the URL Is not changed. When I set username = "Test" inside my route-config, the URL is not changed.
Second, I can't navigate to my other controllers. If I change the URL to http://localhost123/Welcome, nothing happens. It should redirect me to a new page.
What am I doing wrong here?
If I change the order of the routes, I can navigate to other pages, but the username Is not displayed In the URL.
I have googled and all of the answers on this subject says that I should use a route like the one above.
On its own, your routing will not work because if the url was .../Product meaning that you wanted to navigate to the Index() method of ProductController, it would match your first route (and assume "Product" is the username. You need to add a route constraint to your roue definitions that returns true if the username is valid and false if not (in which case it will try the following routes to find a match).
Assuming you have a UserController with the following methods
// match http://..../Bryan
public ActionResult Index(string username)
{
// displays the home page for a user
}
// match http://..../Bryan/Photos
public ActionResult Photos(string username)
{
// displays a users photos
}
Then you route definitions need to be
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "User",
url: "{username}",
defaults: new { controller = "User", action = "Index" },
constraints: new { username = new UserNameConstraint() }
);
routes.MapRoute(
name: "UserPhotos",
url: "{username}/Photos",
defaults: new { controller = "User", action = "Photos" },
constraints: new { username = new UserNameConstraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Test", action = "Index", id = UrlParameter.Optional }
);
}
public class UserNameConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
List<string> users = new List<string>() { "Bryan", "Stephen" };
// Get the username from the url
var username = values["username"].ToString().ToLower();
// Check for a match (assumes case insensitive)
return users.Any(x => x.ToLower() == username);
}
}
}
If the url is .../Bryan, it will match the User route and you will execute the Index() method in UserController (and the value of username will be "Bryan")
If the url is .../Stephen/Photos, it will match the UserPhotos route and you will execute the Photos() method in UserController (and the value of username will be "Stephen")
If the url is .../Product/Details/4, then the route constraint will return false for the first 2 route definitions and you will execute the Details() method of ProductController
If the url is .../Peter or .../Peter/Photos and there is no user with username = "Peter" then it will return 404 Not Found
Note that the the sample code above hard codes the users, but in reality you will call a service that returns a collection containing the valid user names. To avoid hitting the database each request, you should consider using MemoryCache to cache the collection. The code would first check if it exists, and if not populate it, then check if the collection contains the username. You would also need to ensure that the cache was invalidated if a new user was added.
You need to categorize the url for different section of your website so that url pattern matching mechanism go smooth. For example in your case put a category 'profile' or anything other. Now your request url look like http://localhost1234/profile/john and route will be
routes.MapRoute(
name: "users", // Route name
url: "Profile/{username}", // URL with parameters
defaults: new { controller = "Home", action = "Index" } // Parameter defaults
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
For more information follow link Routing in MVC
I'm writing few routes for my MVC application. I have the following routes for my application:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Pages", action = "Index", id = UrlParameter.Optional }
);
The route above is used when I want to access default values like:
www.servicili.com/budget/edit/1
www.servicili.com/professional/view/234
But, I create the following route for a specific purpose:
routes.MapRoute(
name: "Perfil",
url: "{UsuApelido}",
defaults: new { controller = "Perfil", action = "Index"}
);
the route above, is used to access the URL profile of a "plumber" for example:
www.servicili.com/MarkZuckberg
the profile details are on the controller Perfil and Action Index, however, since I wrote this route, all other actions aren't working.
For example: If I try to access the Index action inside another controller, it redirect to Index of Perfil.
--
The question is: Since I wrote a route for a specific Action of a Controller, do I need to write a route for all Actions inside the Controller?
To solve your problem try like this,
First define constraint,
public class PlumberUrlConstraint: IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var db = new YourDbContext();
if (values[parameterName] != null)
{
var UsuApelido = values[parameterName].ToString();
return db.Plumbers.Any(p => p.Name == UsuApelido);
}
return false;
}
}
Define two routes, put "Default" route at 2nd position
routes.MapRoute(
name: "Perfil",
url: "{*UsuApelido}",
defaults: new { controller = "Perfil", action = "Index"},
constraints: new { UsuApelido = new PlumberUrlConstraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Pages", action = "Index", id = UrlParameter.Optional }
);
Now if you have an 'Index' action in 'Perfil' Controller, you can get plumber name like this,
public ActionResult Index(string UsuApelido)
{
//load the content from db with UsuApelido
//display the content with view
}
Hope this help.
I want to keep some custom Url context over all MVC generated urls. There is my situation: Users in several roles are accessing my website over URL with prefix by their roles (i.e. .../admin/{controller}/...). It is a requirement for my project, it has to be this way. I want to keep the user role context in every url generated later in views, controlers, etc. (methods like Html.ActionLink, Url.Action, RedirectTo, ...)
My routes definitions in RouteConfig.cs:
// guest route
routes.MapRoute(
name: "Guest",
url: "guest/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, userContext = UserRoles.Guest }
);
// authentized user
routes.MapRoute(
name: "User",
url: "user/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, userContext = UserRoles.User }
);
// authentized admin
routes.MapRoute(
name: "Admin",
url: "admin/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, userContext = UserRoles.Admin }
);
Then in BaseController (every other controller inherit from that):
protected UserRoles CurrentRole = UserRoles.Admin;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
CurrentRole = (UserRoles)RouteData.Values["userContext"];
base.OnActionExecuting(filterContext);
}
So I have CurrentRole property in every controller/action and view (given CurrentRole to ViewBag). I can make context urls by adding the routeValues new {userContext = CurrentRole} to the url using methods.
BUT!!! I have allready crated this website without the user context and don't want to rewrite every using of method like this or create own custom (Html, Url) extensions to cover every usage of url using method. I want to create some override of Url.GenerateUrl() or something else whitch would keep my user context.
Do you have any ideas?
Thank you very much
Change your route to this so that Html.ActionLink, Url.Action, RedirectTo will use the current user context.
routes.MapRoute(
name: "WithUserContext",
url: "{usercontext}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { usercontext = "guest|user|admin" }
);
My domain used to point to a wordpress site where I had set up specific pages using the following format:
www.mydomain.com/product/awesome-thing
www.mydomain.com/product/another-thing
Recently I transferred my domain and now it points to an MVC version of my site. The links mentioned above are no longer valid, however the wordpress site still exists with a different domain. I'm trying to get my mvc site to absorb the previous links and forward them to
http://mydomain.wordpress.com/product/awesome-thing
http://mydomain.wordpress.com/product/another-thing
what I have right now is the following in the RouteConfig.cs
routes.MapRoute(
name: "product",
url: "product/{id}",
defaults: new { controller = "product", action = "redirect", id = UrlParameter.Optional });
and in my product controller I have the following
public void redirect(string id)
{
if (id == "awesome-thing")
{
Response.Redirect("http://mydomain.wordpress.com/product/awesome-thing ");
}
if (id == "another-thing")
{
Response.Redirect("http://mydomain.wordpress.com/product/another-thing");
}
Response.Redirect(" http://mydomain.wordpress.com/");
}
However my routing in RouteConfig.cs is not linking up correctly with my controller. I keep getting "404 The resource cannot be found" error.
I managed to solve this issue by reordering my map routes. I also changed the code in the controller and the maproute a bit, the following code ended up working.
routes.MapRoute(
name: "productAwesome",
url: "product/awesome-thing",
defaults: new { controller = "product", action = "redirectAwsome" });
routes.MapRoute(
name: "productAnother",
url: "product/another-thing",
defaults: new { controller = "product", action = "redirectAnother" });
//it's important to have the overriding routes before the default definition.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
then in the product controller I added the following:
public class productController : Controller
{
public void redirectAwsome()
{
Response.Redirect("http://mydomain.wordpress.com/product/awesome-thing ");
}
public void redirectAnother()
{
Response.Redirect("http://mydomain.wordpress.com/product/another-thing");
}
}