How do I allow /Controller/Id in ASP.NET MVC? - c#

I want to visit this url
/thread/123
Where 123 is the thread id.
I can't figure out how to set up my routes or whatever it is.
I thought to add thread to my HomeController, but it appears that it only works if I go to /home/thread.
How do I set up this project so the url /thread/123 will work?
I tried /thread as a controller but it seemed like it thought 123 was an Action Method and the other attempt had thread be in Home rather then root.

You want to add a Route designated for this URI
routes.MapRoute(
"Thread", // Route name
"thread/{id}", // URL with parameters*
new { controller = "Thread", action = "Display", id = UrlParameter.Optional }
);
very important: Add your new route ABOVE the "Default" Route!

Related

Correct me on URL routing in MVC issue

This is related my question which i asked in this link correct me on url routing in mvc
Now i came with another problem, so i thought i will ask it as new question.
Now i have following routes in my global.asax file
routes.MapRoute(
"Custom", // Route name
"{action}/{id}", // URL with parameters
new { controller = "Authentication", action = "BigClientLogin", id = UrlParameter.Optional } // Parameter defaults
);
and
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Authentication", action = "BigClientLogin", id = UrlParameter.Optional } // Parameter defaults
);
Now what happens is when i run my solution the URL i am getting is http://localhost:65423/Login this is what i need for my Login Page that is OK. But when i login in as user i am getting "The resource cannot be found" error.
when i checked it i can see that my URL is now changed to http://localhost:65423/Admin/Dashboard
So i think this causing the issue. So this looks the problem related to my global.asax routing.
Can anyone help me to find out what i did wrong.
You have 2 routes with completely optional segments. The issue is that there is no way for the routing framework to differentiate between them.
The only way you can make it work with your existing routes is to specify them explicitly by name (such as when using #Html.RouteLink or #Html.RouteUrl).
#Html.RouteLink("Custom Link 1", "Custom", new { action = "BigClientLogin" })
#Html.RouteLink("Custom Link 2", "Custom", new { action = "Action2" })
#Html.RouteLink("Home Page", "Default", new { controller = "Home", action = "Index" })
#Html.RouteLink("About", "Default", new { controller = "Home", action = "About" })
Doing it that way will function, but is not normal. Typically, there is only one route configured with all defaults for the controller, action, and id and the rest have some explicitly declared segments and/or constraints (segments being preferable).
routes.MapRoute(
"Custom", // Route name
"Custom/{action}/{id}", // URL with parameters
new { controller = "Authentication", action = "BigClientLogin", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Authentication", action = "BigClientLogin", id = UrlParameter.Optional } // Parameter defaults
);
The first route will now only match when the URL starts with /Custom/. If it does not start with custom, it will match the default route.
The trick is to ensure that the routes are listed in the right order and that they only match the URL in specific cases, letting them pass on to the next route in the list if the case is not correct.
It happens because of 2 things.
1st the sequence of Your route
2nd your defaults
So if you end up on 'admin/dashboard' that's not a default, guess you've just put it in your studio to start there?
To get to http://localhost:65423/Login you'll need an action on Your AuthenticationController called 'Login' but it looks like the one You have configured is 'BicClientLogin' so You won't be taken to login unless you specify it, and at that time it has to exist.
To Help You further we need to know what controllers You have and what actions there exists, plus if security is part of your solution and if in case, what is is set to use.

Custom Routing without changing the URL

I have a controller and a view which returns book information when I pass the ID, e.g.:
/Content/Index?id=1
Now I want to make this as a friendly URL to end user. For eg:
Books/BookName (Name of the book the Id 1 is mapped to)
So I added a route values in global.asax as :
route.maprRoute(name:"custom", url:"Books/{bookname}",
defaults: new {controller = "bookMap", action ="index"}
in "BookMap" controller I get the bookname and convert that to the ID (which is 1)
and do a redirectionToAction to Content/Index by passing the ID as a parameter.
This works fine. But the problem is I want to keep the friendly name after redirecting to the view. Now it changes to Content/Index?id=1. But I want to keep the friendly URL which is Books/BookName. How do I achieve this pls.
You can use HttpContext.RewritePath(url) to do a redirect "internally" which keeps the external URL. Use this in place of the RedirectToAction. Note however that it's not properly supported by the MVC framework at this time so it will be a little "hacky" to implement.
You should change your route for this:
routes.MapRoute(
"Books",
"book/{bookname}/{bookid}",
new { controller = "book", action = "Index", bookname = UrlParameter.Optional },
new { id = #"\d+" }
);

MVC4: same url base for different actions

I would like to have 2 routes similar to these:
routes.MapRoute("Detail", "guide/{urlname}", new { controller = "Application", action = "Detail" });
routes.MapRoute("Search", "guide/{keyword1}/{keyword2}", new { controller = "Guide", action = "Index", keyword1 = UrlParameter.Optional, keyword2 = UrlParameter.Optional });
So one route is a detail page that looks up an object in the database based on its url name, and the other route is a search results page based on application-generated keywords, both of which share the same url root (/guide). The two actions are in different controllers. Possible urls are:
/guide/evernote --> should route to the application detail page
/guide --> should route to search results without filter
/guide/iphone --> should route to iphone apps search results
/guide/iphone/medical --> should route to medical iphone apps search results
Obviously, like this, the second route will never be matched for a url like /guide/iphone because the first route will already match the same url.
I don't want to do a redirect in the first action if the controller can't find the object in the database. So what other alternatives are there? Do I need to create a custom RouteHandler or UrlRoutingModule for this or is there a simpler way?
If {urlname} is a url like it implies, you can add a constraint to test if the url matches a regex:
http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-route-constraint-cs
Change the order and it will work:
routes.MapRoute("Search", "guide/{keyword1}/{keyword2}", new { controller = "Guide", action = "Index" });
routes.MapRoute("Detail", "guide/{urlname}", new { controller = "Application", action = "Detail" });

Drop Route Values on Redirect

So, I've been trying for a few hours now to workaround something that should theoretically be very simple. Let's take this sample url:
http://sample.com/products/in/texas/dallas
This maps onto a specific route:
routes.MapRoute(
"products",
"products/in/{state}/{city}",
new { controller = "Products", action = "List", state = UrlParameter.Optional, city = UrlParameter.Optional });
In my action method, I can do lookups to make sure "texas" and "dallas" exist, and that "dallas" exists within Texas. That's all fine and dandy. However, in the situation where the city doesnt exist (either because the geo is incorrect, or mispelled), I want it to back up the state level. Example:
http://sample.com/products/in/texas/dallax
That should issue a redirect to
http://sample.com/products/in/texas
The "easy" way to do this was to simply issue a Redirect call like so:
return Redirect("/products/in/" + stateName);
However, I'm trying to decouple this from the URL structure; for example, if we ever decided to change how the route looks (say, change the pattern to products/around/{state}/{city}), then I would have to know that I need to make updates to this controller to fix the URL redirect.
If I can make a solution that just inspects the route values and can figure things out, then I don't have to worry if I change the route pattern, because the route values could still be figured out.
Ideally, I would have liked to do something like this:
return RedirectToRoute(new { controller = "Products", action = "List", state = state });
(Note, that is is a simplified example; the "required" route pieces like controller name and action method name would be determined by Generic argument and Expression inspection respectively).
That actually performs the redirect, HOWEVER, the route values from the current request get appended onto the redirect and thus you get in a redirect loop (note that I didn't include the city route value in the route object). How do I stop the "city" route value from being included in this redirect?
I've tried the following things to get rid of the route value:
Compose my own RouteValueDictionary / anonymous route data object and pass that to the overload of RedirectToRoute.
Create my own action result to inspect RouteTable.Routes and find the route myself, and do the replacement of the tokens myself. This seems the most "kludgy", and would seem to be re-inventing the wheel.
Make a method like RedirectWithout that takes a key value and calls RedirectToRouteResult.RouteValues.Remove(key) - which also didnt work.
I've also attempted to add a null value for the key I don't want to add; however, this alters the route to something that isnt correct - ie new { controller = "Products", action = "List", state = stateName, city = (string)null } issues a redirect to /Products/List?state=Texas which is not the right URL.
It all seems to stem from the RedirectToRoute taking the current request context to construct the virtual path data. Is there a workaround?
If you were using T4MVC, you should be able to do something like this:
return RedirectToAction(MVC.Products.List(state, null));
Have you tried this?
return RedirectToRoute(new
{
controller = "Products",
action = "List",
state = state,
city = null,
});
Reply to comments
Maybe MVC is confused because your optional parameter is not at the end. The below should work with the above RedirectToRoute that specifies city = null:
routes.MapRoute(
"products",
"products/in/{state}/{city}",
new
{
controller = "Products",
action = "List",
// state = UrlParameter.Optional, // only let the last parameter be optional
city = UrlParameter.Optional
});
You can then add another route to handle URL's where state is optional:
routes.MapRoute(null, // I never name my routes
"products/in/{state}",
new
{
controller = "Products",
action = "List",
state = UrlParameter.Optional
});

Dynamic ASP.net MVC Routing

I have a need for some dynamic routing. So my routes would look like this:
{UserName}
{UserName}/Edit/{id}
{UserName}/Delete/{id}
Where the users would be routed to the user controller. But I still want to maintain routes to controls like:
{Controller}/Edit/{id}
{Controller}/Delete/{id}
So basically I want it to direct to the physical controller say called OrdersController for edit delete but if someone navigates to /jdoe/ it sends it to the user controller.
How do I do this in my routes?
You need to create multiple routes, and keep them in the appropriate order
// one route for Users
routes.MapRoute("Users",
"{username}/{action}/{id}",
new { controller = "Users", action = "Index", username = string.Empty, id = UrlParameter.Optional },
new { id = #"\d+" }
);
// one route for everything else
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional},
new { id = #"\d+" }
);
It's not "really" as easy as above, but that's the jist of it. You would need too add a RouteConstraint to validate usernames.
Lastly, if you're using the username parameter, then why do you need the id? Just a thought.
Aside:
If you look at the user section here on StackOverflow, you'll see the routing look more like this.
users/{id}/{username}
users/{id}/edit
users/{id}/delete
I would personally say that this is a lot less work to achieve... but hey, that's just me.

Categories

Resources