Can I control (manipulate) the address bar using MVC .NET - asp.net-mvc

I would like to invoke this URL:
www.example.com/home/brand1
but have the resulting URL be:
www.example.com/brand1
I want to do this with multiple brands. I know I can have an action in the home controller for each brand which redirects to a brand controller but I don't want a controller for each brand. I imagine I can do this with routing but just don't know how.

routes.MapRoute(
"Default",
"{brandName}",
new { controller = "Home", action = "YourBrandAction", brandName = "" }
); // inside RegisterRoutes method
//Your Controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult YourBrandAction(String brandName)
{
//your controller logic...
return View(yourBrandModel);
}
}
You must be very careful with the above route configuration. Some URLs, e.g. www.example.com/login will not direct you to the login page but treat 'login' as a brand name.

Related

How is URL Routing handled when the URLs are 3+ levels deep? (RESTful URLs)

So let's say you need to have these URLs in your RESTful routing:
/Company/About
/Company/Product/View
/Company/Product/Edit
/Company/Contact/View
/Company/Contact/Edit
I assume that all of these actions would need to be in the same controller (i.e. Company) and I also assume that the routing would look something like this in Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("mission", "Company/Product/{action}/{id}",
new { controller = "Company", id = "" });
routes.MapRoute("mission", "Company/Contract/{action}/{id}",
new { controller = "Company", id = "" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
The problem arises when trying to cram all of this into the one controller (and, again, my assumption is that having all of this in the same controller is correct) because the controller action names will mess everything up. Here is what I mean by this:
In this example, we will need controller actions named the following in the Company controller:
About (for Company/About)
ProductView (for /Company/Product/View)
ProductEdit (for /Company/Product/Edit)
ContactView (for /Company/Contact/View)
ContactEdit (for /Company/Contact/Edit)
These action names, however, don't match what is going to be expected by the routing table. For example, if the user browses to /Company/Product/View the routing would direct flow to the action named "View" in the Company controller. What if the user browses to /Company/Contact/View? The routing will route flow to the same "View" table in the Company controller. How would one route these requests to different Views?
How would you solve this issue? My goal is to be as RESTful as possible.
First to be RESTful you shouldn't be including the actions in the route names. Instead of specifiying edit or view in the route you should be routing through http verbs. I would also suggest using attribute routing
Here's an example controller:
[RoutePrefix("api/company")]
public class CompanyController : ApiController
{
[Route("product"), HttpGet]
public Product ViewProduct()
{
}
[Route("product"), HttpPut]
public Product EditProduct()
{
}
[Route("contact"), HttpGet]
public Product ViewContact()
{
}
[Route("contact"), HttpPut]
public Product EditContact()
{
}
}

How do I use routes for multi-tenancy in all but one controller?

Our app has multiple tenants. Every tenant has a short code assigned to them that users know them by. I want to use that code in my URLs as a route parameter, and have Ninject inject a DbContext with the tenant's database connection string into the tenant-specific controllers.
So for examine I have a CarController, and every tenant has their own products. The URLs would look like {tenantcode}/{controller}/{action}. I understand how to do this part.
However, I have several controllers that should NOT be instanced by tenant. Specifically, the home controller, and account controller for login/registration. These don't matter.
So example URLs I need:
myapp.com/ - HomeController
myapp.com/Account/Login - AccountController
myapp.com/GM/Car/Add - CarController that has GM's DbContext injected
myapp.com/Ford/Car/Add - CarController that has Ford's DbContext injected
How can I exclude certain controllers from routes? Running ASP.NET MVC 5.
Many thanks to Darko Z for starting me in the right direction. I ended up using a hybrid of traditional routes, and the new attribute based routing in MVC 5.
First, the "excluded" routes got decorated with the new RouteAttribute class
public class HomeController : Controller
{
private readonly TenantContext context;
public HomeController(TenantContext Context)
{
this.context = Context;
}
//
// GET: http://myapp.com/
// By decorating just this action with an empty RouteAttribute, we make it the "start page"
[Route]
public ActionResult Index(bool Error = false)
{
// Look up and make a nice list of the tenants this user can access
var tenantQuery =
from u in context.Users
where u.UserId == userId
from t in u.Tenants
select new
{
t.Id,
t.Name,
};
return View(tenantQuery);
}
}
// By decorating this whole controller with RouteAttribute, all /Account URLs wind up here
[Route("Account/{action}")]
public class AccountController : Controller
{
//
// GET: /Account/LogOn
public ActionResult LogOn()
{
return View();
}
//
// POST: /Account/LogOn
[HttpPost]
public ActionResult LogOn(LogOnViewModel model, string ReturnUrl)
{
// Log on logic here
}
}
Next, I register the tenant generic route that Darko Z suggested. It's important to call MapMvcAttributeRoutes() before making other routes. This is because my attribute based routes are the "exceptions", and like he said, those exceptions have to be at the top to make sure they are picked up first.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// exceptions are the attribute-based routes
routes.MapMvcAttributeRoutes();
// tenant code is the default route
routes.MapRoute(
name: "Tenant",
url: "{tenantcode}/{controller}/{action}/{id}",
defaults: new { controller = "TenantHome", action = "Index", id = UrlParameter.Optional }
);
}
}
So as I'm sure you know you specify routes in MVC in the order from most specific to most generic. So in your case I would do something like this:
//exclusions - basically hardcoded, pacing this at the top will
//ensure that these will be picked up first. Of course this means
//you must make sure that tenant codes cannot be the same as any
//controller name here
routes.MapRoute(
"Home",
"Home/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
routes.MapRoute(
"Account",
"Account/{action}/{id}",
new { controller = "Account", action = "Index", id = "" }
);
//tenant generic route
routes.MapRoute(
"Default",
"{tenantcode}/{controller}/{action}",
new { tenantcode = "Default", controller = "Tenant", action = "Index" }
);
//default route
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
This is obviously only good if there are less excluded controllers than controllers that need the tenant code. If not then you can take the opposite approach and reverse the above. Main takeaway here is that (happy to be proven wrong) there is no way to have a generic ignore within an AddRoute call. While there is an IgnoreRoute, that just completely doesn't apply any routing rules and is used for static resources. Hope that helps.

App entry point - child actions are not allowed to perform redirect actions

The index action on my home controller is the entry point for my MVC 3 app. When I try to redirect to another view from within this action i'm getting the 'child actions are not allowed to perform redirect actions' error on an Html.Action on my layout view. I'm not redirecting from a child view so can anyone explain why I'm getting this error. This only happens on the Index action of my home controller, any other action on any other controller, including the home controller works fine.
Home controller:
public class HomeController : Controller
{
public ActionResult Index()
{
if ([some logic to lookup user] = 0)
return View("UserNotFound");
else
return view();
}
}
#Html.Action in layout:
#Html.Action("BuildMenu", "Menu", new { menu = #ViewBag.Menu })
Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Menu controller:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
[ChildActionOnly]
public ActionResult BuildMenu(string menu)
{
var model = new ViewModels.MenuViewModel()
{
Menu = menu,
ShowCreateNew = BL.Permissions.CheckUserPermission(this.UserDetails.UserId, this.UserDetails.RoleId, BL.Common.permission_AddNewRisk),
ShowAdmin = BL.Permissions.CheckUserPermission(this.UserDetails.UserId, this.UserDetails.RoleId, BL.Common.permission_AdminRights)
};
return PartialView("_Menu", model);
}
Is there a redirect inside the BuildMenu action of the Menu controller that only occurs when the user is not found?
That would cause this error as that is the child action referenced.
I have just tested exactly this and got the error:
Child actions are not allowed to perform redirect actions
If the this.UserDetails value is null, which based on the condition in the Home Index I assume is possible then that would cause an exception which is possibly being caught by a globally applied attribute which contains a redirect.
I tested this as well and got the same error.

How do I mix short URLs and regular URLs in ASP.NET MVC?

I have a site with the normal, default route and several controllers. I would like to distribute "short URL" links that can link back to the home/index action. For example, I can do
/MySite - takes you to Home/Index as default
/MySite/SomeController/SomeAction - takes you to the specified controller/action as default
but I would also like to do:
/MySite/SomeID - takes you to Home/Index with the id param supplied.
I can add a "shortUrl" route and distribute a url like "/MySite/ShortUrl/SomeID", but is there any other way to use an "id-only" url like the one above?
The problem you've got with doing something like this is that the following would then be ambiguous:
/MySite/SomeID
/MySite/SomeController
How do you expect to be able to differentiate between the two? If you don't mind the second being impossible (i.e. you are happy always specifying an action when you specify a controller), you could try something like this:
routes.MapRoute(
"ShortUrl",
"{id}",
new { controller = "Home", action = "Index", id = Url.OptionalParameter }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = Url.OptionalParameter }
);
Requesting /MySite/SomeID should then take you to the same action as MySite/Home/Index/SomeID.
If you need to be able to specify either and ID or a controller (with default action), you could do something like the following (also using the above routing):
public class HomeController : Controller
{
public ActionResult Index(string id)
{
// If the ID represents something, show that something.
if (IdMatchesSomeResource(id))
{
// Do something
return View();
}
// Otherwise, treat it as a request for a controller.
else
{
return RedirectToAction("Index", id);
}
}
}

How to skip controller name while using RedirectToAction?

I have accountController class and its have login & home views.
[HandleError]
public class accountController : Controller
{
[HttpPost]
public ActionResult login(LoginModal model, string returnUrl)
{
//Authentication
return RedirectToAction("home");
}
public ActionResult home()
{
return View();
}
}
------------------------------
-----------------------------
Global.asax have Route entry.. so my urls is
http://lmenaria.com/login
http://lmenaria.com/home
routes.MapRoute(null, "home", new { controller = "account", action = "home" });
routes.MapRoute(null, "login", new { controller = "account", action = "login" });
When I tried the both URL on browser they are working fine. But when login success then its go to http://lmenaria.com/account/home
So how can I remove "account" from this url. this is going when I used return RedirectToAction("home"); and getting 404 error.
So please let me know how can I resolved that issue. I don't need Controller Name in url.
Thanks
Laxmilal Menaria
routes.MapRoute("home", "home", new { controller = "account", action = "home" });
I tried with Above MapRoute & use RedirectToRoute instead of RedirectTOAction and its work.
Thanks.
If you want to default the controller to "account", without showing it in your URL, then you can do something like
routes.MapRoute(null, "{action}", new { controller = "account" });
if you want, you can use a default value for action too
routes.MapRoute(null, "{action}", new { controller = "account", action = "home" });
I'm editing my answer to let you know that you don't need to explicitly define each route as you're doing. MVC routes match patterns. So, instead of
routes.MapRoute(null, "home", new { controller = "account", action = "home" });
routes.MapRoute(null, "login", new { controller = "account", action = "login" });
use just
routes.MapRoute(null, "{action}", new { controller = "account" });
And pay attention to the routes order if you define more than one pattern, because order matters. MVC will use the first pattern that matches your URL.
I think the real issue is that you either didn't create or removed the home controller.
From your description it really sounds like you should be calling RedirectToAction for the Home controller Index action.
you have [HttpPost] on your Login action. Which should be the problem. I just run your codes without [HttpPost] attribute. it is working. when you type http://lmenaria.com/login, your "login" action will not be fired, because of the attribute. so, there must be some other routes that does the routing.

Resources