ASP.NET MVC Routing wrong - asp.net-mvc-3

I'm having some troubles when trying to use routes in ASP.NET MVC 3.
I'm in this URL:
mysite/InitialPage/123456 <- partner ID
Routing it...
routes.MapRoute(
"InitialPageRoute",
"InitialPage/{partnerID}",
new { controller = "InitialPage", action = "Index" }
);
OK, it works fine.
When I put a Html.ActionLink to redirect to another page...
#Html.ActionLink("Another copy of some bill", "CopyOfBill", "Payment", new { partnerID = ViewBag.PartnerID })
And routing this way...
routes.MapRoute(
"CopyOfBillRoute",
"Payment/CopyOfBill/{partnerID}",
new { controller = "Payment", action = "CopyOfBill" }
);
And doesn't work.
Anyone have any idea?
Thanks!!!

It might be best to use a RouteLink in this case (name the route explicitly), since both the controller and action are hard-coded.
#Html.RouteLink("Another copy of some bill", "CopyOfBillRoute",
new { partnerID = ViewBag.PartnerID })

Use this overload
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
So your code will be
#Html.ActionLink("Another copy of some bill", "CopyOfBill", "Payment",
new { partnerID = ViewBag.PartnerID },null)
Assuming you do not want to specify any HTML attributes for the anchor tag. If you want, replace the last parameter (null) with the HTML Attributes.

first you write your route in Global.asax at the top . top you make your first route
routes.MapRoute(
"CopyOfBillRoute",
"Payment/CopyOfBill/{partnerID}",
new { controller = "Payment", action = "CopyOfBill" }
);
and use your route like this
#Html.RouteLink("click", "Regis",new {id=>id.Id})
i think this will help you

Related

Whats wrong with my routes and actions?

I recently asked a question based on how to create pages based on the content table which contains the following: Title and Content. I followed the steps, to my understanding, in the answer that was given.
I created a route like so:
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
);
routes.MapRoute(
"ContentManagement",
"{title}",
new { controller = "ContentManagement", action = "Index", title = "{title}" }
);
}
I am assuming I can do routes like this? where I can set up multiple routes? I am also assuming I can pass the title to to the controller action like I have done?
I then created the model:
namespace LocApp.Models
{
public class ContentManagement
{
public int id { get; set; }
[Required]
public string title { get; set; }
public string content { get; set; }
}
}
from that I created a controller with an index action that looks as such:
public ViewResult Index(string title)
{
using (var db = new LocAppContext())
{
var content = (from c in db.Contents
where c.title == title
select c).ToList();
return View(content);
}
}
So then I created some content with the title of "bla" so when I visit site.com/bla I get an error that it cant find "bla/"
Can some one tell me what I am doing wrong? I would also, if you are familiar with the default layout of a asp.net mvc project with the tabs at the top, create a set of tabs that lead to the pages, based on the title in the database
The main issue is that when you are using the title, the routing engine is matching it to the first route and trying to find a controller by that title. We have implemented something similar and found that by explicitly defining what controllers are valid for the default route, it then processed request appropriately. I gave an example of the controllers that we allow to fit our default route below (Home, Help and Error).
You probably also want to prevent people from giving the content the same TITLE as your root level controllers as that would blow this up pretty well.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional},
new {controller = "Home|Error|Help"},
new[] {"UI_WWW.Controllers"});
routes.MapRoute(
"ContentManagement",
"{title}",
new {controller = "ContentManagement", action = "Index"});
}
}

MVC3 links SEO - how to make calls from one controller to other controller

I have one problem, I am not sure how to explain but I will try.
I followed this: http://www.deliveron.com/blog/post/SEO-Friendly-Routes-with-ASPnet-MVC.aspx
And I was able to achieve what they describe.
But if I have a page where i wish to call action from other controller, it doesn't work.
It doens't show the link in this way: "this-is-my-link" in the URL.
I don't know what do I do wrong?
in 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
);
routes.MapRoute(
"Default2",
"{controller}/{action}/{id}/{pageTitle}",
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
pageTitle = UrlParameter.Optional
}
);
}
Views/Help/FAQ
#Html.ActionLink(FaqStrings.ContactUs, "ContactUs", "Home", new { id = 1, pageTitle = "link text".ToSeoUrl() })
It says that it can't resolve the ContactUs. Instead of the actions from HomeController, it sees the actions of the HelpController.
In HomeController
[AllowAnonymous]
public ActionResult ContactUs()
{
var model = new ViewModelContactUs
{
Resultmessage = string.Empty,
Youremail = string.Empty,
Yourmessage = string.Empty,
Yourname = string.Empty
};
return View(model);
}
[AllowAnonymous]
[HttpPost]
public ActionResult ContactUs(ViewModelContactUs model)
{
Log.DebugFormat("HomeController-ContactUs()");
........
var model2 = new ViewModelContactUs
{
......
};
ModelState.Clear();
return View(model2);
}
Try moving your controller into the RouteValues dictionary part of your #Html.ActionLink
#Html.ActionLink(FaqStrings.ContactUs, "ContactUs", new {controller="Home", id = 1, pageTitle = "link text".ToSeoUrl() })
EDIT
To address the logic, it has to do with the available signatures of the Html.ActionLink method. Here is the MSDN for information but...
The method signature that you were using was Html.ActionLink("Link Text", "Action", "Contoller", RouteValues, HtmlAttributes). Since you were not passing HtmlAttributes, it was matching up wrong and generating the wrong link. By either moving the controller into the RouteValuesDictionary or passing a , null at the end of your call should solve it. But, I personally don't like throwing nulls around unless I need to, so I typically just define the controller in the RouteValuesDictionary.
If you were not passing any RouteValues, then a call to Html.ActionLink("Link Text", Action, Controller) works with no issues.
Hope that clears it up a little! :)
The underlying problem is that:
#Html.ActionLink(FaqStrings.ContactUs,
"ContactUs",
"Home",
new { id = 1, pageTitle = "link text".ToSeoUrl() })
Will produce an Anchor which when clicked will produce an HTTP GET, however you're method is requiring HTTP POST:
[AllowAnonymous]
[HttpPost] // <-----POST
public ActionResult ContactUs(ViewModelContactUs model)

ASP.NET MVC basic routing with parameters

I have been trying to learn ASP.NET MVC 3 and things are going well apart from the routing aspect, whatever I try I just can't seem to get them quite right.
I have an ActionLink on the main page:
#Html.ActionLink("Contracts", "List", "Contract",
new { User.Identity.Name, page=1 })
Which is meant to access this method in the ContractController:
public ViewResult List(string user, int page = 1)
{
//snip
}
My routes are:
routes.MapRoute(
null,
"Page{page}",
new { Controller = "Contract", action = "List" }
);
routes.MapRoute(
null,
"Page{page}",
new { Controller = "Contract", action = "List", user = "", page = 1 }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
The link now will return a 404 error as it can't find the action 'List' in the controller 'Home', which obviously means it didn't use either of the first routes.
Everything worked before I tried to add parameters to the ActionLink, so basically, what am I doing wrong?
Thanks very much.
Alex,
You're doing all the other bits absolutely correctly, however the actionlink has a missing parameter, try this for your actionlink:
#Html.ActionLink("Contracts", "List", "Contract",
new { User.Identity.Name, page = 1 }, null)
Adding the null as the final param (htmlAttributes) is all that's missing for you in this scenario (there are 9 overloads for Html.ActionLink, so it's VERY easy to miss the correct implementation).

RouteHandler vs ControllerFactory

new to asp.net mvc (using v3 + razor) and am wondering how to best solve a problem with creating dynamic routes based on a database. Essentially, the main site navigation will be entered into a database and I want to load them up as routes. i.e. - Load Category list from database, then append the routes to the routing engine if possible...
mysite.com/cars
mysite.com/televisions
mysite.com/computers
etc....
Each category after the slash comes from the db, but, there are regular entries like /about and /contactus that will not be in the database and have been statically entered in the global.asax... my question is:
For the dynamic database URLs should I use a custom RouteHandler or pehaps create a ControllerFactory that will match and handle the requests for the entries loaded from the database. Is it possible to have the DefaultControllerFactory handle the routing if my RouteHandler or CustomControllerFactory don't find the route in the list from the database? Thanks for any help, very first project with this so I'm not sure what the best route is ;) no pun intended...
Update:
Tried using a route constraint that pulls from the database but it conflicts with the default route now... here is my custom constraint and routes:
public class CategoryListConstraint : IRouteConstraint
{
public CategoryListConstraint()
{
var repo = new Repository<Topic>();
var cats = repo.All();
var values = new List<string>();
foreach (var c in cats)
{
values.Add(c.URI.Replace("/", "").Replace("?", ""));
}
this._values = values.ToArray<string>();
}
private string[] _values;
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
// Get the value called "parameterName" from the
// RouteValueDictionary called "value"
string value = values[parameterName].ToString();
// Return true is the list of allowed values contains
// this value.
return _values.Contains(value);
}
}
and here are the routes:
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Categories",
"{category}/{*values}",
new { controller = "Category", action = "List" },
new CategoryListConstraint()
);
Routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
The home page www.mysite.com loads using the Default route. All the URLs that match the constraint list are loaded by the category route... but if I have the www.mysite.com/admin or www.mysite.com/aboutus these are getting picked up by the Categories route even though the values are not in the constraint list. Confused...
What about something like this?
Categories controller:
public ActionResult List(string category)
{
var products = _repo.Get(category); // however you are getting your data
return View(products);
}
Routes
routers.MapRoute(
"About",
"About",
new { controller = "Home", action = "About" });
//... other static routes
routes.MapRoute(
"CategoriesList",
"{id}",
new { controller = "Categories", action = "List" },
new { id = #"\w+" });
The incoming URL is tested against each Route rule to see if it matches - and if a Route rule matches then that rule (and its associated RouteHandler) is the one that is used to process the request (and all subsequent rules are ignored). This means that you want to typically structure your routing Rules in a "most specific to least specific" order
source
Found the exact solution I was looking for. Code is below. I managed to avoid using Controller Factories or implementing a custom IRouteHandler by using extending the RouteBase class which worked perfectly and allows me to pass control down to the default mvc route is something specific isn't hit. BTW - constraints ended up not working properly as the broke the controllers associated with the default route (although the default route was getting hit)
public class CustomRoutingEngine : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeHandler = new MvcRouteHandler();
var currentRoute = new Route("{controller}/{*URI}", routeHandler);
var routeData = new RouteData(currentRoute, routeHandler);
// implement caching here
var list = GetConstraintList();
// set your values dynamically here
routeData.Values["controller"] = "Category";
// or
routeData.Values.Add("action", "List");
// return the route, or null to have it passed to the next routing engine in the list
var url = Util.StripSlashOnFrontAndBack(httpContext.Request.Path.ToLower()).Split('/')[0];
if (list.Contains(url))
return routeData;
return null; // have another route handle the routing
}
protected List<string> GetConstraintList()
{
using (var repo = new RavenRepository<Topic>())
{
var tops = repo.Query().Where(x => x.Hidden == false).ToList()
.Select(x=>x.Name.ToLower());
List<string> list = new List<string>();
list.AddRange(tops);
repo.Dispose();
return list ?? new List<string>();
}
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//implement this to return url's for routes, or null to just pass it on
return null;
}
}
Then my register routes method looks like so:
Routes.Clear();
// Set Defaults
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.Add(new App.Helpers.CustomRoutingEngine());
Routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

Issue With ViewBag and Routes (MVC 3 - RC2)

I am having an issue with MVC-3 generating outgoing routes for me.
This is the address of the page I am on for both scenarios: http://localhost:1283/Conflict/Create/1200/300
Here are the map routes:
routes.MapRoute(
null, // Route name
"{controller}/{action}/{custId}/{projId}", // URL with parameters
null, // Parameter defaults
new { custId = #"\d+", projId = #"\d+" }
);
routes.MapRoute(
null, // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Scenario 1:
From the controller:
public ActionResult Create(int custId, int projId)
{
return View();
}
From the view:
#Html.ActionLink("Back to List", "Index", "Conflict", new { custId = ViewBag.custId, projId = ViewBag.projId }, null)
The resulting link that gets created.
http://localhost:1283/Conflict?custId=1200&projId=300
If I change the controller code to read as follows:
public ActionResult Create(int custId, int projId)
{
ViewBag.custId = custId;
ViewBag.projId = projId;
return View();
}
I didn't make any changes in the view, only added those two lines to the controller and the following link is created:
http://localhost:1283/Conflict/Index/1200/300
What am I missing here? This is consistent behavior, I was able to reproduce this in other areas of my application. The "solution" is obvious, but my question is why?
What's happening is the "?custId=1200&projId=300" part of your link is coming over from the link you used to GET the page you're on. So the Html.ActionLink call is doing this:
generate the /Conflict/Index path
look for the custId and projId in the ViewBag and finds the query string instead
just appends your query string
In the second scenario, you're actually providing values, so the link is generating normally. Hope that helps.

Resources