Paging and routing in ASP.Net MVC - asp.net-mvc

I am following Martijn Boland's 'Paging with ASP.NET MVC'. And while helpful it has raised a couple of issues I don't understand.
Martijn says:
Internally, the pager uses
RouteTable.Routes.GetVirtualPath() to
render the url’s so the page url’s can
be configured via routing to create
nice looking url’s like for example
‘/Categories/Shoes/Page/1′ instead of
‘/Paging/ViewByCategory?name=Shoes&page=1′.
This is the is what he is talking about:
private string GeneratePageLink(string linkText, int pageNumber)
{
var pageLinkValueDictionary = new RouteValueDictionary(this.linkWithoutPageValuesDictionary);
pageLinkValueDictionary.Add("page", pageNumber);
//var virtualPathData = this.viewContext.RouteData.Route.GetVirtualPath(this.viewContext, pageLinkValueDictionary);
var virtualPathData = RouteTable.Routes.GetVirtualPath(this.viewContext.RequestContext, pageLinkValueDictionary);
if (virtualPathData != null)
{
string linkFormat = "{1}";
return String.Format(linkFormat, virtualPathData.VirtualPath, linkText);
}
else
{
return null;
}
}
How does this work? When I use it virtualPathData.VirtualPath just brings back a url representing the first route in my routing table with a 'page' param on the end rather then a url representing the current context.
Also what would the routing look like to change this ‘/Paging/ViewByCategory?name=Shoes&page=1′ to this ‘/Categories/Shoes/Page/1′ ?

I assume You have Paging controller and this controller has ViewByCategory action.
ViewByCategory looks like:
public ActionResult ViewByCategory(string categoryName, int? page)
{
....
}
Routing will look like
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"RouteByCategory",
"Categories/{categoryName}/Page/{page}",
new { controller = "Paging", action = "ViewByCategory" }
);
routes.MapRoute(
"RouteByCategoryFirstPage",
"Categories/{categoryName}",
new { controller = "Paging", action = "ViewByCategory", page = 1 }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
GeneratePageLink will return link in ‘/Categories/Shoes/Page/1′ format, because it is first matching route pattern in routing table.

Related

How to get the add the query string to actionLink?

The below link gives be the following url: http://localhost:11111/files/Details/3
#Html.ActionLink("Details", "Details", "mycontroller", new { id = item.id },null)
But I'm trying to have a url parameter like this http://localhost:11111/files/Details?id=3 or http://localhost:11111/files/Details.aspx?id=3
How do I get the actionlink to show the url like details?i=3
Here is my controller View:
public ActionResult Details(int? id)
{
...
return View();
}
Why would you like to see the parameter's name in the link?
Asp.Net MVC uses user-friendly URLs.
If you have created a project in Visual Studio using the MVC template, probally your routes, by default, are configured to interpret the parameter after the controller/action/ like the id.
So the id parameter's value in your action will be automatically replaced, by model binding, with the id number present in you URL.
The routing codes can be found under the RegisterRoutes method in the Global.asax file of our project.
I see a cookie already in the RegisterRoutes method.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults);
Using the MapRoute method above, we defined a new route.
Sample ;
public class HaberController : Controller
{
public ActionResult Listele()
{
// Listing codes will be written
return View("Listele");
}
public ActionResult Detay(string HaberId)
{
// Detail codes will be written
return View("Detay");
}
}
We go to our Global.asax file and edit it as follows.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"HaberListeleme",
"Haber",
new { controller = "Haber", action = "Listele" }
);
routes.MapRoute(
"HaberDetay",
"Haber/{id}",
new { controller = "Haber", action = "Detay" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
If we do the routing as follows:
routes.MapRoute(
"HaberDetay",
"Haber/{*Id}",
new { controller = "Haber", action = "Detay" }
);
So if we write * by putting the character next to our parameter name, it will be sent to the related parameter of the Detail method in the Controller, no matter what it says after the News / url tab.
For example:
http://www.doguhanaydeniz.com/Haber/Turkiye/Guncel/34389
If a URL is requested as Turkey / current / 34389 will be sent as a parameter.

MVC Route static value

I'm not sure if this is possible using mvc routing, I haven't been able to find a similar example.
I have about 5~ controller actions that are the same method, so I'd like to refactor them into a single action. I'd like to pass an enum value to the controller to tell it what path it should pass to lower layers.
Example:
public ActionResult ViewPage(int id, PageEnum page) {
var model = MyService.GetModelForTemplate(id, page);
return ("ViewPage", model);
}
Then the user could access this either through /PagesTypeOne/ViewPage/, or /PagesTypeTwo/ViewPage/. Both routes leading to the same endpoint.
Route table attempt:
routes.MapRoute(
name: "typeOne",
url: "PagesTypeOne/{action}/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional, page = PageEnum.TypeOne, }
);
routes.MapRoute(
name: "typeTwo",
url: "PagesTypeTwo/{action}/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional, page = PageEnum.TypeTwo, }
);
This obviously isn't working.
Is there a way I can do something like this? It would make my code much more concise.
screen != page, so if the property on the anonymous type matches the parameter it will work:
public ActionResult ViewPage(int id, PageEnum screen) {
var model = MyService.GetModelForTemplate(screen);
return ("ViewPage", model);
}
Updated: Created a empty application and it work flawlessly:
namespace MvcApplication6
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Test1",
"PagesTypeOne/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional,
page = PageEnum.PageOne } // Parameter defaults
);
routes.MapRoute(
"Test2",
"PagesTypeTwo/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional,
page = PageEnum.PageOne } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
public enum PageEnum
{
Undefined,
PageOne,
PageTwo
}
Controller:
namespace MvcApplication6.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ViewPage(int id, PageEnum page)
{
var debug = 1; // break point
return new EmptyResult();
}
}
}
you can make it completely dynamic with one route definition:
// the route must be defined as the first route
routes.MapRoute(
name: "typeTwo",
url: "{page}/ViewPage/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional },
new { page= getPageTypes() }
);
the getPageTypes method:
private static string getPageTypes()
{
var pageTypes = Enum.GetNames(typeof(PageEnum));
return string.Join("|", pageTypes );
}
but PagesTypeOne/ViewPage/4 part must match the enum's name.

MVC mapping routes to navigation menu and avoiding hard-coding links

I have setup my routes so that I have the following flat URL structure:
mywebsite/service-option-one (goes to Home Controller, Option1 action)
mywebsite/service-option-two (goes to Home Controller, Option2 action)
mywebsite/ (goes to Home Controller, Index)
mywebsite/about (goes to Home Controller, Index with path=about)
mywebsite/contact (goes to Home Controller, Index with path=contact)
This is important as I have a lot of content views and do not want to have individual actions for these generic information pages, the simple code for the resolving the view is at the end of this post.
When building the menu the MVC Html.ActionLink helpers, but they give the incorrect addresses for generic content which makes sense as these actions do not exist!
Given my address scheme, how can is there a helper method I can use to set my anchor link targets or do i just have to resort to hard coding in html (i.e. <a href="~/contact>Contact us</a>)?
// note that the order of the routes is very important!
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// error route
routes.MapRoute(
"Error", // Route name
"Oops", // URL with parameters
new { controller = "Home", action = "Error" }
);
routes.MapRoute(
"Service1",
"service-option-one",
new { controller = "Home", action = "Option1" }
);
routes.MapRoute(
"Service2",
"service-option-two",
new { controller = "Home", action = "Option1" }
);
// default home controller route for general site content (/content), uses default path value set to Index
routes.MapRoute(
name: "Catchall",
url: "{path}",
defaults: new { controller = "Home", action = "Index", path = "Index" }
);
// home page route, blank url (/)
routes.MapRoute(
"HomePage", // Route name
"", // URL with parameters
new { controller = "Home", action = "Index" }
);
// default route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "{controller}", action = "{action}", id = UrlParameter.Optional } // Parameter defaults
);
}
public class HomeController : Controller
{
private bool ViewExists(string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}
public ActionResult Index(string path)
{
System.Diagnostics.Debug.WriteLine("looking for..." + path);
if (ViewExists(path)==true)
{
System.Diagnostics.Debug.WriteLine("general controller action...");
return View(path);
}
else
{
System.Diagnostics.Debug.WriteLine("page not found...");
return RedirectToRoute("Error");
}
}
public ActionResult Error()
{
return View();
}
public ActionResult Option1()
{
return View();
}
public ActionResult Option2()
{
return View();
}
}
You're approaching the use of the ActionLink helper from the opposite position that you should be when using helpers. You're trying to tell Razor what the routed url should be for the link, when in reality that's what Razor is going to be doing for YOU. So for the example of mywebsite/service-option-one, the only place you should have service-option-one written down is in your routes map. In your views, you should be doing
#Html.ActionLink("The Anchor Text You Want To Display","Option1","Home")
Then when Razor renders the view, it will translate to
The Anchor Text You Want To Display
http://msdn.microsoft.com/en-us/library/dd505070(v=vs.118).aspx

URl rewriting in mvc

Hy
i had write below code
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Home", // Route name
"", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"Controller_Action", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { id = UrlParameter.Optional } // Parameter defaults
);
foreach (var route in GetDefaultRoutes())
{
routes.Add(route);
}
routes.MapRoute(
"UserPage", // Route name
"{id}", // URL with parameters
new { controller = "Home", action = "Get" } // Parameter defaults
);
}
private static IEnumerable<Route> GetDefaultRoutes()
{
//My controllers assembly (can be get also by name)
Assembly assembly = typeof(test1.Controllers.HomeController).Assembly;
// get all the controllers that are public and not abstract
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)) && t.IsPublic && !t.IsAbstract);
// run for each controller type
foreach (var type in types)
{
//Get the controller name - each controller should end with the word Controller
string controller = type.Name.Substring(0, type.Name.IndexOf("Controller"));
// create the default
RouteValueDictionary routeDictionary = new RouteValueDictionary
{
{"controller", controller}, // the controller name
{"action", "index"} // the default method
};
yield return new Route(controller, routeDictionary, new MvcRouteHandler());
}
}
i am new to mvc,i want to rewrite my url somthing like this,suppose my url is like www.myhouse.com/product/index/1 then i want to display only www.myhouse.com/prduct-name for better seo performance,i am using mvc4 beta,i had also one through URL Rewriting in .Net MVC but it is not working for me....
but i don't know how to pass pass value to this method.
After lots of searching on the internet, i got my solution
add below code to global.asax
routes.MapRoute(
"Home", // Route name
"", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"jats", // Route name
"{jats}", // URL with parameters
new { controller = "Home", action = "Content" } // Parameter defaults
);
then add below code to view:
#Html.ActionLink("test", "Content", new { jats= "test-test" })
add below code to HomeController:
public ActionResult Content(string jats)
{
return View();
}
then you done...now URL is same as you pass in query string...so your controller name and query string parameter will not display.

ASP.NET MVC Route with dash

I've got ASP.NET MVC routing question.
I prepared following routing table to map such url
mywebsite/mycontroller/myaction/14-longandprettyseoname
to parameters:
14 => id (integer)
longandprettyseoname -> seo_name (string)
routes.MapRoute(
"myname",
"mycontroller/myaction/{id}-{seo_name}",
new { controller = "mycontroller", action = "myaction", id = 0, seo_name = (string)null });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" });
It works for URL above but it has problems for following type of urls
mywebsite/mycontroller/myaction/14-long-and-pretty-seo-name
Is that possible to make it working?
EDIT:
"mycontroller/myaction/{seo_name}-{id}"
seems to be working
The most obvious way to do this is to use constraints.
Since that your id is an integer, you can add a constraint which will look for an integer value:
new { id = #"\d+" }
and here is the whole route:
routes.MapRoute("myname","mycontroller/myaction/{id}-{seo_name}",
new { controller = "mycontroller", action = "myaction" },
new { id = #"\d+"});
My solution is define route as:
routes.MapRoute("myname","mycontroller/myaction/{id}",
new { controller = "mycontroller", action = "myaction"});
and parse id and seoname manualy using Regex in HTTP handler:
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
var match = System.Text.RegularExpressions.Regex.Match((string)routeData.Values["id"], #"^(?<id>\d+)-(?<seoname>[\S\s]*)$");
if (!match.Success)
{
context.Response.StatusCode = 400;
context.Response.StatusDescription = "Bad Request";
return;
}
int id = Int32.Parse(match.Groups["id"].Value);
string seoname = match.Groups["seoname"].Value;
I don't think the route will be distinguishable as it will not be able to figure which "-" to split at to specify the {id} and the {seo-name}.
How about using underscores for your SEO name? Or you could just use the SEO name as the actual {id}. If the SEO name is something that is going to be unique, this is a very viable option you can use as a pseudo primary key to that entry in your db (assuming it's pulling something from a DB)
Also, utilize Phil Haack's route debugger to see what works and doesn't work.
Define a specific route such as:
routes.MapRoute(
"TandC", // Route controllerName
"CommonPath/{controller}/Terms-and-Conditions", // URL with parameters
new { controller = "Home", action = "Terms_and_Conditions" } // Parameter defaults
);
But this route has to be registered BEFORE your default route.
What you could do is create a custom controller factory. That way you can have custom code to decide which controller needs to be called when.
public class CustomControllerFactory : IControllerFactory
{
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
//string language = requestContext.HttpContext.Request.Headers["Accept-Language"];
//can be used to translate controller name and get correct controller even when url is in foreign language
//format controller name
controllerName = String.Format("MyNamespace.Controllers.{0}Controller",controllerName.Replace("-","_"));
IController controller = Activator.CreateInstance(Type.GetType(controllerName)) as IController;
controller.ActionInvoker = new CustomInvoker(); //only when using custominvoker for actionname rewriting
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
To use this custom controllerfactory, you should add this in your global.asax
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
Note that this only works for the controller, not for the actions... To hook up custom rewriting on actions before they get executed, use this code:
public class CustomInvoker : ControllerActionInvoker
{
#region IActionInvoker Members
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
return base.InvokeAction(controllerContext, actionName.Replace("-", "_"));
}
#endregion
}
I got most of this code from this blog and adjusted it to my needs. In my case, I want dashes to separate words in my controller name but you can't create an action with a dash in the name.
Hope this helps!

Resources