My app needs to have the ability to let users navigate to a page using the foo.com/pubid convention where pubid is a randomly generated string such as "wgwrfwrcwrf".
My Home controller has an Index ActionResult defined:
public ActionResult Index(string id)
{
//do stuff
}
When I navigate to this action on my site without specifying an ID everything is fine. I return the view associated to home. This is what bit.ly does. I am trying to translate that ID inside my app.
However if I request a URL such as foo.com/wgwrfwrcwrf I get a 404. I need to be able to catch this string to send it to the right view. Does this require a custom route?
routes.MapRoute(
"id", // Route name
"{id}", // id route
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You should write a catch-all route like this...
routes.MapRoute("catchall", "{*id}",
new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
Enjoy :)
Related
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);
}
}
}
I've managed to get my Home controller working by only using the action name to access it, but as soon as I add another controller and try do the same thing by adding another route declaration the same at my Home route declaration, all the routes default to the Home controller.
routes.MapRoute("HomeTest", "{action}/{id}", new { controller = "Home", action = "TestHome", id = UrlParameter.Optional });
routes.MapRoute("TestTest", "{action}/{id}", new { controller = "Test", action = "Test", id = UrlParameter.Optional });
public class HomeController : Controller
{
public ActionResult TestHome(int? id)
{
return View();
}
}
public class TestController : Controller
{
public ActionResult Test(int? id)
{
return View();
}
}
Is there any way I can access both controllers without including the controller name in the url?
I also have the default route included if that makes any difference. It's under these two routes.
Routes in MVC are compared in the order you add them. For your code sample, this means that all requests will be compared to the "HomeTest" route first, so any matches will follow that route. The "TestTest" route has the exact same format, which means that it will never be used because any matches will have already used the "HomeTest" route. If you want to get to get to a controller's actions without using the controller name, there must be something in the route to tell it which controller to use. This doesn't have to be the actual controller name - you can do this:
routes.MapRoute("TestTest", "keyWord/{action}/{id}", new { controller = "Test", action = "Test", id = UrlParameter.Optional });
routes.MapRoute("UsersTest", "otherKeyWord/{action}/{id}", new { controller = "Users", action = "Test", id = UrlParameter.Optional });
where the keywords basically act as aliases to hide your controller names. You can even go one step further and mask your action names as well, which can let you use multiple controllers under one URL root:
routes.MapRoute("TestTest", "test/key1/{id}", new { controller = "Test", action = "Test", id = UrlParameter.Optional });
routes.MapRoute("UsersTest", "test/key2/{id}", new { controller = "Users", action = "Test", id = UrlParameter.Optional });
Doing something like this can let you give your users a much tidier URL structure to help them navigate your site and understand where links will take them (potentially also handy for bookmarking) but it does mean you have to manually define your URLs, which is more work.
With the way you have defined your url patterns routing engine will always redirect all the actions matching pattern {action}/{id} to Home controller.
What are you looking for is probably something like:
routes.MapRoute("HomeTest", "testhome/{id}", new { controller = "Home", action = "TestHome", id = UrlParameter.Optional });
routes.MapRoute("TestTest", "test/{id}", new { controller = "Test", action = "Test", id = UrlParameter.Optional });
I have an MVC website which used to use URLs in the standard format of: Controller/Action.
Recently, I have changed it to: Site/Controller/Action.
The problem is, there are several links to my site out there which follow the old format, and I want to redirect them accordingly.
for example: mydomain.com/Home/CustomerSearch now should go to mydomain.com/Online/Home/CustomerSearch
whereas: mydomain.com/AffiliatesHome/CustomerSearch now should go to mydomain.com/Affiliate/AffiliatesHome/CustomerSearch
How can I get it to handle the redirecting by putting in the extra routing, depending on the link they came in by?
The current routing I am using is:
routes.MapRoute(
"Default", // Route name
"{site}/{controller}/{action}/{id}",
new {site="IS", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Since I do not really see an schema in your old to new URL mapping I would suggest to add routes that match the old Controller/Action Schema and map them to the new Site/Controller/Action route schema.
So you could add the following routes
routes.MapRoute(
"LegacyHome",
"Home/{action}/{id}",
new { site="Online", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"LegacyAffiliates",
"AffiliatesHome/{action}/{id}",
new { site="Affiliate", controller = "AffiliatesHome", action = "Index", id = UrlParameter.Optional }
);
From an SEO standpoint this is not ideal because you have different URLs for the same page. A permanent redirect via status code 301 and the new URL passed in the location is better suited.
You could build a redirect controller and use the legacy routes to map legacy URLs to the redirect controller somehow like this
routes.MapRoute(
"LegacyHome",
"Home/{newAction}/{id}",
new { controller = "Redirect", action = "Redirect", newSite = "Online", newController="Home", newAction = "Index", id = UrlParameter.Optional }
);
Code of the redirect controller
public class RedirectController : Controller
{
public ActionResult Redirect(string newSite, string newController, string newAction)
{
var routeValues = new RouteValueDictionary(
new
{
site = newSite,
controller = newController,
action = newAction
});
if (RouteData.Values["id"] != null)
{
routeValues.Add("id", RouteData.Values["id"]);
}
return RedirectToRoutePermanent(routeValues);
}
}
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.
I have a url
http://www.roadkillwiki.org/Page/Index/documentation
which I want to turn into
http://www.roadkillwiki.org/Page/documentation
That could also be something like http://www.roadkillwiki.org/Page/my-url-with-spaces - the parameter is a string. The route setup I've tried is:
routes.MapRoute(
"ControllerDefault",
"{controller}/{id}",
new { controller = "Page", action = "Index", id = UrlParameter.Optional }
);
However this is interfering with the default "id" route that MVC projects come with. Is there any way of achieving this?
You don't need to lose the default route. The key to avoiding your routes interfere with each other is to order them so the more specific rules precede the less specific ones. For example:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{slug}",
new { controller = "Page", action = "Index" }
);
// Default MVC route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Then your PageController would look like this:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(string slug)
{
// find page by slug
}
}
That said, I would strongly advice you to do this instead:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{id}/{slug}",
new { controller = "Page", action = "Index", slug = UrlParameter.Optional }
);
// MVC's default route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And your PageController:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(int id)
{
// find page by ID
}
}
By including the page ID either at the beginning of your URL (like StackOverflow does) or at the end, you can then just ignore the slug, and instead retrieve your pages by ID. This will save you a ton of headaches if your users change the page name. I have gone through this and it's painful; you basically have to keep a record of all names your pages have had in the past, just so your visitors/search engines don't get a 404 every time a page is renamed.
Hope this helps.
If you don't need a default route that came with project template you can set up one like this:
routes.MapRoute(
"ControllerDefault",
"{controller}/{pagename}",
new { controller = "Page", action = "Index" }
);
And than in your controller you would have an action:
public ActionResult Index(string pagename)
{
//do something
}