I read this article about how you can prefix routes in ruby on rails. I want to be able to do the same thing with asp.net mvc
So I want to be able to define a route like :
/photographers/1/photos/2 //photo 2 of photographer with id 1
/photographers/1/photos //all of photographer with id 1
Any tips ?
EDIT:
"photographers/{id}/photos/{photoID}" - seems to do the job quite ok, BUT how can I support
RedirectToAction<PhotosController>(x => x.Add());
I would like to redirect to : /photographers/1/photos/add
Define your route like this:
routes.MapRoute(
"Photographers",
"photographers/{id}/photos/{photoID}",
new { controller = "Photographers", action = "Photo", photoID = null });
Then define your controller action like this:
public ActionResult Photo(int id, int? photoID)
{
// If photoID is not null, show just that photo.
// Otherwise, show all photographs.
}
You could use regex routing or use wildcards in your routing table so that the {id*} matches the /1/photos/2 for the photographers default controller, parse the string, and redirect to an appropriate action.
Also take a look at this post about nested resources.
RouteTable.Routes.Add(
new Route { Url = "events/[eventId]/tickets/[action]/[id]",
Defaults = new { controller = "Tickets",
action = "List", id = (string)null },
RouteHandler = typeof(MvcRouteHandler) });
Related
I have a view that renders a list of news, in that i have an href tag, and i need to call a Detail method and pass it the news id, now i have a querystring in my url like that
News/Details?id=x... but i need something like News/Details/Category/Title-of-something, a friendly url with news category, name and without the id
this is my action, it works but i get that querystring
foreach (var item in Model)
{
Read More
}
I was trying with something like, with a Url.RouteUrl
routes.MapRoute(
name: "Details",
url: "{controller}/{action}/{id}/{category}/{newsName}",
defaults: new { controller = "News", action = "Details"}
);
but it never goes to Details actionresult, and also i need to pass the id parameter for showing some news Details, but i don't want to display it in the friendly url. Im really confused how to achieve it. Thanks in advance
Try this:
#Html.RouteLink("Read More", "Details", new { action = "Details", id = item.newsId, category = item.categoryName, newsName = item.newsName })
Use :
Read More
Make sure your route is not overridden by other route. So put your route first in RoutConfig.cs
Change your code to:
foreach (var item in Model)
{
Read More
}//change name=item.newsName to newsName=item.newsName
Also make sure that your controller has correct signature as per your routing details:
public class NewsController : Controller
{
public ActionResult Details(int id, string category, string newsName )
}
First URL Correct
Read More
You may Route Debugger whenever yo see issue related to routes
I'm trying to learn asp.net mvc, and almost everywhere I see route description with three components like /Controller/Action/{anyParams}
I'd like to know if I can map a route similar to,
/Folder(or namespace)/Controller/Action/params...
ex:
/Admin/Student/Edit/id
/ABC/Faculty/Add/`
/XYZ/Student/Edit/id
or in general,
/XYZ/Controller1/Action/{param}
Yep the second parameter in the MapRoutes function (usually in Global.asax.cs is Url and this can be any pattern you want. something like
routes.MapRoute("MyRoute", "XYZ/Controller1/Action/{param}",
new {controller = "Controller1", action = "Action"}});
should do the trick.
You can make your routes as complex as you want.
F.e. the following route:
routes.MapRoute("some-route", "products/detail/order/{id}/{name}/",
new { controller = "Products", action = "Order" },
new { id = "^\d+" });
will route to the following function:
public class ProductsController : Controller {
public ActionResult Order (int id, string name) {
}
}
So you can specify as many parameters as you want, and they will be passed into your action as function parameters.
I am building a Help Desk Ticket system for a client using ASP.NET MVC 1.0 / C#. I have implemented Steven Sanderson's "App Areas in ASP.NET MVC, Take 2" and it is working great.
In my Globabl.asax page I have some routes defined as such:
public static void RegisterRoutes(RouteCollection routes)
{
// Routing config for the HelpDesk area
routes.CreateArea("HelpDesk", "ProjectName.Areas.HelpDesk.Controllers",
routes.MapRoute(null, "HelpDesk/{controller}/{action}", new { controller = "Ticket", action = "Index" }),
routes.MapRoute(null, "HelpDesk/Ticket/Details/{TicketId}", new { controller = "Ticket", action = "Details", TicketId = "TicketId" })
);
}
So, if I enter "http://localhost/HelpDesk/Ticket/Details/12" in the browser address bar manually, I get the results I expect. Here is my controller:
public ActionResult Details(int TicketId)
{
hd_Ticket ticket = ticketRepository.GetTicket(TicketId);
if (ticket == null)
return View("NotFound");
else
return View(ticket);
}
In my view I have:
<%= Html.ActionLink(item.Subject, "Details", new { item.TicketId } )%>
But that code generates "http://localhost/HelpDesk/Ticket/Details?TicketId=12" which also returns the expected results. My Question is...
How do I define an ActionLink when using Steven Sanderson's Areas that will create a clean URL like: "http://localhost/HelpDesk/Ticket/Details/12" ?
Try
<%= Html.ActionLink(item.Subject, "Details", new { TicketId = item.TicketId } )%>
The ActionLink method expects a dictionary with keys that match the parameter names. (Note that passing an anonymous object is a convenience for this). Anything else I believe it will just tag onto the end of the URL.
EDIT: The reason that this isn't working for you is because your first route matches and takes precedence (controller and action), but defines no TicketId parameter. You need to switch the order of your routes. You should always put your most specific routes first.
Try
<%= Html.ActionLink(item.Subject, "Details", new { TicketId=item.TicketId } )%>
I think Womp has it ...
Oh and while you are swapping your routes try
routes.MapRoute(null, "HelpDesk/Ticket/Details/{TicketId}", new { controller = "Ticket", action = "Details"})
I think the , TicketId = "id" is messing things up
Hope that helps,
Dan
I am trying to create a route with a Username...
So the URL would be mydomain.com/abrudtkhul (abrudtkhul being the username)
My application will have public profiles based on usernames (Ex: http://delicious.com/abrudtkuhl). I want to replicate this URL scheme.
How can I structure this in ASP.Net MVC? I am using Membership/Roles Providers too.
Here's what you want to do, first define your route map:
routes.MapRoute(
"Users",
"{username}",
new { controller = "User", action="index", username=""});
What this allows you to do is to setup the following convention:
Controller: User (the UserController type)
Action: Index (this is mapped to the Index method of UserController)
Username: This is the parameter for the Index method
So when you request the url http://mydomain.com/javier this will be translated to the call for UserController.Index(string username) where username is set to the value of javier.
Now since you're planning on using the MembershipProvider classes, you want to something more like this:
public ActionResult Index(MembershipUser usr)
{
ViewData["Welcome"] = "Viewing " + usr.UserName;
return View();
}
In order to do this, you will need to use a ModelBinder to do the work of, well, binding from a username to a MembershipUser type. To do this, you will need to create your own ModelBinder type and apply it to the user parameter of the Index method. Your class can look something like this:
public class UserBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
var request = bindingContext.HttpContext.Request;
var username = request["username"];
MembershipUser user = Membership.GetUser(username);
return new ModelBinderResult(user);
}
}
This allows you to change the declaration of the Index method to be:
public ActionResult Index([ModelBinder(typeof(UserBinder))]
MembershipUser usr)
{
ViewData["Welcome"] = "Viewing " + usr.Username;
return View();
}
As you can see, we've applied the [ModelBinder(typeof(UserBinder))] attribute to the method's parameter. This means that before your method is called the logic of your UserBinder type will be called so by the time the method gets called you will have a valid instance of your MembershipUser type.
You might want to consider not allowing usernames of certain types if you want to have some other functional controllers like Account, Admin, Profile, Settings, etc. Also you might want your static content not to trigger the "username" route. In order to achieve that kind of functionality (similar to how twitter urls are processed) you could use the following Routes:
// do not route the following
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("content/{*pathInfo}");
routes.IgnoreRoute("images/{*pathInfo}");
// route the following based on the controller constraints
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
, new { controller = #"(admin|help|profile|settings)" } // Constraints
);
// this will catch the remaining allowed usernames
routes.MapRoute(
"Users",
"{username}",
new { controller = "Users", action = "View", username = "" }
);
Then you will need to have a controller for each of the tokens in the constraint string (e.g. admin, help, profile, settings), as well as a controller named Users, and of course the default controller of Home in this example.
If you have a lot of usernames you don't want to allow, then you might consider a more dynamic approach by creating a custom route handler.
You could have a route that looks like:
{username}
with the defaults of
Controller = "Users"
For the finished product of:
routes.MapRoute(
"Users",
"{username}",
new { controller = "Users" }
So that the Username is the only url parameter, the MVC assumes it passes it to the users controller.
Are you trying to pass it as a parameter to a Controller?
Theoretically you could do something like this:
routes.MapRoute("name", "{user}/{controller}/{action}", new { controller = "blah", action = "blah", user = "" })
I'm not too experienced with ASP.NET routing, but I figure that should work.
routes.MapRoute(
"Users",
"{username}",
new { controller = "Users", action="ShowUser", username=""});
Ok, what this will do is default the username parameter to the value of {username}
so even though you've written username="" it'll know to pass the value of {username} .. by virtue of the strings match.
This would expect
for the url form
http://website.com/username
Users Contoller
ShowUser Action (method in controller)
username parameter on the ShowUser action (method)
I'm trying to implement routing such as the following:
posts/535434/This-is-a-post-title
posts/tagged/tags+here
// Matches {controller}/{action}/{id} - Default
// Displays all posts with the specified tags
// uses PostsController : ActionTagged(string tags)
posts?pageSize=50&pageIndex=4
// Matches {controller}/{action}/{id} - Default
// Displays all posts
// uses PostsController : Index(int? pageSize, int? pageIndex)
Here's the problem I want to do this:
posts/39423/this-is-a-post-title-here
// Typically this is implemented using an action like 'Details'
// and would normally look like : posts/details/5
I can't seem to get the routing working right. I tried something like this:
{controller}/{id}/{description}
and set the default action to be "Display" which works, but then won't allow me to navigate to other named actions like "Tagged".
What am I missing?
Thanks!
Two things:
First, you should always order your routes in decreasing specificity (e.g. most specific case first, least specific case last) so that routes will "fall through", if one doesn't match it will try the next.
So we want to define {controller}/{postid}/... (must be a postid) before we define {controller}/{action}/... (could be anything else)
Next, we want to be able to specify that if the provided value for postid does not look like a Post ID, the route should fail and fall through to the next one. We can do this by creating an IRouteConstraint class:
public class PostIDConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
//if input looks like a post id, return true.
//otherwise, false
}
}
We can add it to the route definition like so:
routes.MapRoute(
"Default",
"{controller}/{postid}/{description}",
new { controller = "Posts", action = "Display", id = 0 },
new { postid = new PostIDConstraint() }
);
I'm not 100% I understand your question, but it sounds like you can just define a couple different routes.
routes.MapRoute("PostId", "posts/{id}/{title}",
new { Controller = "Posts", Action = "DisplayPost", id = 0, title = "" },
new { id = #"\d+" });
routes.MapRoute("TaggedPosts", "posts/tagged/{tags}",
new { Controller = "Posts", Action = "DisplayTagged", tags = "" });
routes.MapRoute("Default", "posts",
new { Controller = "Posts", Action = "Index" });
You can use regular expressions to validate parameters like I used for id in the first route, or if you want some better validation do something like Rex M posted. The querystring parameters pageSize and pageIndex don't need to be included in your route; they will just be passed in to your Index method as long as the parameter names match.
The part of the url that's the "description" actually isn't used.
For example, this post is 519222 and I can still get to it using the url: Having issues with MVC Routing