ASP.NET MVC - create custom route without a fallback - c#

I'm creating an MVC project and I have the following link that leads to a list of websites for the logged in user:
http://[server]/Members/Websites
now I would like to add a new custom route under the websites part of the user that will lead to a service connection page:
http://[server]/Members/Websites/Connect-to-service/999
where "999" is the id of the website that is being connected.
I've defined the following route for that manned (placing it above the default route):
routes.MapRoute(
name: "myRoute",
url: "Members/Websites/Connect-to-service/{id}",
defaults: new { controller = "Members", action = "Connect_To_Service" }
);
now, if I enter the url "http://[server]/Members/Websites/Connect-to-service/999" it loads my page fine.
if I enter the url "http://[server]/Members/Websites/Connect-to-service/" it loads the websites page.
what I would like to have is when loading "http://[server]/Members/Websites/Connect-to-service/" it will either redirect to "http://[server]/Members/Websites/" (in the browser url), or throw a page not found error.
can this be done?
thanks

You could configure this in the Connect_To_Service action:
public ActionResult Connect_To_Service(int? id = null)
{
if (!id.HasValue)
{
// Either:
return RedirectToAction("Websites", "Members");
// Or:
return HttpNotFound();
}
// An id is provided. Process..
}
You might need to change your route by adding a default value for id:
defaults: new { controller = "Members", action = "Connect_To_Service", id = UrlParameter.Optional }

Related

If Route doesn't exist -> go to other route using its default action?

So I'm having a little problem here with routing.
There are two parts to this web application:
  1. Brochure / Display Website
  2. Internal Site / Client Application
We wanted a way to release changes for the brochure without having to do a whole release of said Web application.
Visiting existing named views will take the user to a brochure page, however if it doesn't exist, it will act like they are a client and will redirect them to their company's login screen.
Global.asax:
//if view doesnt exist then url is a client and should be redirected
routes.MapRoute(
name: "Brochure",
url: "{id}",
defaults: new { controller = "brochure", action = "Brochure", id = "Index" },
namespaces: new[] { "Web.Areas.Brochure.Controllers" }
);
//This is home page
routes.MapRoute(
name: "HomeDefault",
url: "{client}/{action}",
defaults: new { controller = "home", action = "index" },
namespaces: new string[] { "Web.Controllers" }
);
Controller:
/// <summary> Check if the view exists in our brochure list </summary>
private bool ViewExists(string name) {
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}
/// <summary> Generic action result routing for all pages.
/// If the view doesn't exist in the brochure area, then redirect to interal web
/// This way, even when we add new pages to the brochure, there is no need to re-compile & release the whole Web project. </summary>
public ActionResult Brochure(string id) {
if (ViewExists(id)) {
return View(id);
}
return RedirectToRoute("HomeDefault", new { client = id });
}
This code works fine up until we log in and go to the landing page. It seems to keep the Brochure action in the route and doesn't want to go to the subsequent controller which results in a 500 error.
e.g. 'domain/client/Brochure' when it needs to be: 'domain/client/Index'
Things tried but not worked:
Changing RedirectToRoute() to a RedirectToAction() - this results in a
finite loop of going back to the ActionResult Brochure(). So
changing controllers through that didn't work.
Create an ActionResult called Brochure() inside the 'HomeController'. It
doesn't even get hit.
Passed in namespaces for RedirectToRoute() as an attribute. I knew this would probably not work, but it was worth a try.
So the question is:
How can I get the route to act properly?
If you can restrict id to some subset of all values you can add that constraints to route (i.e. numbers only) to let default handle the rest.
routes.MapRoute(
name: "Brochure",
url: "{id}",
defaults: new { controller = "brochure", action = "Brochure", id = "Index" },
namespaces: new[] { "Web.Areas.Brochure.Controllers" }
constraints : new { category = #"\d+"}
);
If you can't statically determine restrictions - automatically redirecting in your BrochureController similar to your current code would work. The only problem with sample in the question is it hits the same route again and goes into infinite redirect loop - redirect to Url that does not match first rule:
// may need to remove defaults from second route
return RedirectToRoute("HomeDefault", new { client = id, action = "index" });
If standard constraints do not work and you must keep single segment in url - use custom constraints - implement IRouteConstraint and use it in first route. See Creating custom constraints.
There are several issues with your configuration. I can explain what is wrong with it, but I am not sure I can set you on the right track because you didn't provide the all of the URLs (at least not all of them from what I can tell).
Issues
Your Brouchure route, which has 1 optional URL segment named {id}, will match any URL that is 0 or 1 segments (such as / and /client). The fact that it matches your home page (and you have another route that is named HomeDefault that will never be given the chance to match the home page) leads me to believe this wasn't intended. You can make the {id} value required by removing the default value id = "Index".
The Brouchure route has a namespace that indicates it is probably in an Area. To properly register the area, you have to make the last line of that route ).DataTokens["area"] = "Brochure"; or alternatively put it into the /Areas/Brouchure/AreaRegistration.cs file, which will do that for you.
The only way to get to the HomeDefault route is to supply a 2 segment URL (such as /client/Index, which will take you to the Index method on the HomeController). The example URLs you have provided have 3 segments. Neither of the routes you have provided will match a URL with 3 segments, so if these URLs are not getting 404 errors they are obviously matching a route that you haven't provided in your question. In other words, you are looking for the problem in the wrong place.
If you provide your entire route configuration including all Area routes and AttributeRouting routes (including the line that registers them), as well as a complete description of what URL should go to what action method, I am sure you will get more helpful answers.
So the question is:
How can I get the route to act properly?
Unknown. Until you describe what properly is.
Related: Why map special routes first before common routes in asp.net mvc?
Two ways I could have solved this issue:
Way 1
I reviewed the redirect and just passed in an action in order to get a route that has 2 segments in the url. i.e. client/Index. The Index action now handles logins - going past a custom controller.
public class HomeController : CustomController
public ActionResult Brochure(string id, string action) {
if (ViewExists(id)) {
return View(id);
}
return RedirectToAction("Index", "Home", new { client = id, action = "Index" });
}
Way 2
(from #Alexei_Levenkov)
Create a custom Route constraint so the route will be ignored if the view cannot be found.
namespace Web.Contraints {
public class BrochureConstraint : IRouteConstraint {
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
//Create our 'fake' controllerContext as we cannot access ControllerContext here
HttpContextWrapper context = new HttpContextWrapper(HttpContext.Current);
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "brochure");
ControllerContext controllerContext = new ControllerContext(new RequestContext(context, routeData), new BrochureController());
//Check if our view exists in the folder -> if so the route is valid - return true
ViewEngineResult result = ViewEngines.Engines.FindView(controllerContext, "~/Areas/Brochure/Views/Brochure/" + values["id"] + ".cshtml", null);
return result.View != null;
}
}
}
namespace Web {
public class MvcApplication : System.Web.HttpApplication {
//If view doesnt exist then url is a client so use the 'HomeDefault' route below
routes.MapRoute(
name: "Brochure",
url: "{id}",
defaults: new { controller = "brochure", action = "Brochure", id = "Index" },
namespaces: new[] { "Web.Areas.Brochure.Controllers" },
constraints: new { isBrochure = new BrochureConstraint() }
);
//This is home page for client
routes.MapRoute(
name: "HomeDefault",
url: "{client}/{action}",
defaults: new { controller = "home", action = "index" },
namespaces: new string[] { "Web.Controllers" }
);
}
}
I hope this helps someone else out there.

How to have a profile url like facebook. (asp.net)

HI i am new to aspdotnet and want to ask how do I able to have a facebook like profile link for each of the registered user in the database.
example:
https://www.facebook.com/james
As you can see after .com there is a unique name. but my question: Is that name a folder or some kind of auto generated link?? How can I implement this kind of link for each of the registered user in my database?
well i can easy do it wit GET but I want to hide the id from the url.
Take a look at your RouteConfig.cs file in your App_Start folder.
The default route (www.facebook.com) is set to your HomeController.Index() method. You can tell that by the "defaults" parameter.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
In the example that you provided, "james" would be a string parameter for that Index method. Obviously, the signature to the method would be:
public ActionResult Index(string id) {
// Do stuff
}

Create a LoadUser controller with custom routing in MVC4

I'm trying to create custom urls for my LoadUser controller. I want the url to be
site.com/LoadUser/1.
The LoadUserByID route is above my default route. I keep getting the default error page
"Error. An error occurred while processing your request."
Can someone please point me in the right direction?
My code is as follows:
RouteConfig.cs
routes.MapRoute(
name: "LoadUserByID",
url: "LoadUser/{id}",
defaults: new { controller = "LoadUser", action = "ID", id = UrlParameter.Optional }
);
LoadUserController.cs
//
// GET: /LoadUser/[userid]
public ActionResult ID(int id)
{
ViewBag.Message = "ID page";
return View();
}
UPDATE
Code is fine and I'm new to MVC. I did not have a View created for LoadUser. I thought I could use the default view. Make sure to have a view created that correlates to your Controller/Action.

Parameter only route always being triggered MVC3

I have two very simple routes
routes.MapRoute(
"post", // Route name
postPage + "/{slug}", // URL with parameters
new { controller = "Home", action = "Article" } // Parameter defaults
);
routes.MapRoute(
"page", // Route name
"{slug}", // URL with parameters
new { controller = "Home", action = "Page", slug = homePage} // Parameter defaults
);
And here is my controller logic
public ActionResult Article(string slug)
{
return View(repo.GetPost(slug));
}
public ActionResult Page(string slug)
{
if (slug.ToLower() == MetaData.PostsPage.ToLower())
return View("listPosts", repo.GetAllPosts());
else
return View("page", repo.GetPage(slug));
}
homePage and postPage are set from value's in the database. Allowing the user to define the default page as well as the page to show posts.
My issue occurs when adding an area named "Admin". I get a controller added to my RouteTable
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
Now when a user Access Admin/Account/Logon the page loads fine, but my debugger still tries to go into the Home controller and the Page action. But the RouteDebugger says it doesn't match the current request. I'm puzzled on how to fix this.
RouteDebugger screenshot: http://i.stack.imgur.com/7cpHm.png
Debugger going into my HomeControler Page AtionResult: http://i.stack.imgur.com/uSJBK.png
Actually the problem is, Area routes are overriding the global routes, to distinguish both the routes set the relevant namespace of area's controller in the context.MapRoute method in adminAreaRegistraton.cs file. i.e.
context.MapRoute(
"admin_default",
"admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
null,
new string[] { "MVCApplication1.Areas.admin.Controllers" }
);
I found out the issue.
I had a favicon.ico set in the main area of my site, but not the Admin area.
So when I went to the Admin area the browser made a request for favicon.ico that got picked up by that route. Thats why my routes looked fine in the RouteDebugger, because they were.
Thanks for the help Kundan!

How do I make this ASP.NET MVC Route for a twitter-style URL work?

trying to map the following style of route: http://site.com/username in the same way that you can do http://www.twitter.com/user
My initial solution was to have these routes:
//site.com/rathboma - maps to user details for rathboma
routes.MapRoute("Users", "{id}", new { controller = "Users", action = "Details" });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "oneday" } // Parameter defaults
);
And that was working fine, until I tried to do the following in my 'Links' controller:
public ActionResult Details(string id)
{
int newId;
if (!int.TryParse(id, out newId))
return RedirectToAction("Index");
WebLink results = Service.GetWebLink(newId, 5);
if (results == null)
return RedirectToAction("Index");
return View(results);
}
These RedirectToAction methods try and return the browser to http://site.com/Users (I do have a users controller) instead of directing to http://site.com/Links/index
Why is this is happening?
How should I be organizing my routes to make this work properly?
I'm happy sacrificing http://site.com/links and moving to http://site.com/links/index if I have to. But how would I enforce that?
Thanks to all for any help
EDIT:
I know what's causing this, it's trying to redirect to http://site.com/links (the index page), but links is being picked up as a username and redirecting to /users/details, when it can't find the user 'links' it tries to redirect to the UsersController Index action which maps to /users, and the cycle continues ('users' is not a user it can find so redirects infinately).
So I guess My sub-question is: how to I make mvc always use /links/index instead of just using /links for the index page?
Try adding this route before your Users route:
routes.MapRoute("Links",
"{controller}/{id}",
new { controller = "Links", action = "Details" });
This should then work for
http://mysite.com/Links/id
&
http://mysite.com/username
I believe changing RedirectToAction("Index"); to RedirectToAction("Index", "Links"); in your Links controller should solve the issue without having to change your routes.
The problem is you have two very greedy routes. What I'd do is to break apart the default route to less greedy routes like this :
routes.MapRoute("Links",
"Links/{id}",
new { controller = "Links", action = "Index" });
routes.MapRoute("Users",
"{id}",
new { controller = "Users", action = "Details" });
routes.MapRoute("Default",
"",
new { controller = "Home", action = "Index" });
Causing the urls to be like the following :
site.com/links/5 - hits the Links controller
site.com/name - hits the Users controller
site.com/ - hits the home controller

Categories