ASP.NET MVC to ignore ".html" at the end of all url
I am new to asp.net mvc and now struggling with url routing. I'm using asp.net mvc 3 RC2.
How can I create a url routing that IGNORES the very end extension in url. the extension can be: .html
, .aspx
, .php
, .anything
.
For example, these urls:
/Home.html
/Home.en
/Home.fr
/Home
should go to Home
controller?
one more example:
/Home/About.html
/Home/About.en
/Home开发者_JAVA技巧/About.fr
/Home/About
should go to Home
controller and About
action.
thank you :)
I'm not sure if you're using IIS7, but if so, then I would recommend a rewrite rule which checks for urls ending in .xyz
and then doing a rewrites for them without the .xyz
.
Something like this:
<rewrite>
<rules>
<rule name="HtmlRewrite">
<match url="(.*)(\.\w+)$" />
<action type="Rewrite" url="{R:1}" />
</rule>
</rules>
</rewrite>
This will handle the use cases you suggested. Anything that ends with an extension and some characters will be rewritten to a url without the extension. The benefit of this is that you will only need one route because everything goes into your application without one.
You just need to tweak the default route in Global.asax.cs, try this:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}.{extension}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
The {extension} value from the url will be included in the route data, but you can just safely ignore it if you don't need it
Either create your own route class, or use this regex route implementation: http://blog.sb2.fr/post/2009/01/03/Regular-Expression-MapRoute-With-ASPNET-MVC.aspx
You could handle this in IIS instead of ASP.NET MVC using IIS Url rewriting. See for example: http://learn.iis.net/page.aspx/496/iis-url-rewriting-and-aspnet-routing/
I started to work on this question as a weekend assignment :D
below code will work as requested in question. please refer below references
1] MyUrlRoute Class : RouteBase
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcIgnoreUrl
{
#region //References
// SO question /http://stackoverflow.com/questions/4449449/asp-net-mvc-to-ignore-html-at-the-end-of-all-url
// Implementing Custom Base entry - Pro Asp.Net MVc Framework
//- http://books.google.com/books?id=tD3FfFcnJxYC&pg=PA251&lpg=PA251&dq=.net+RouteBase&source=bl&ots=IQhFwmGOVw&sig=0TgcFFgWyFRVpXgfGY1dIUc0VX4&hl=en&ei=z61UTMKwF4aWsgPHs7XbAg&sa=X&oi=book_result&ct=result&resnum=6&ved=0CC4Q6AEwBQ#v=onepage&q=.net%20RouteBase&f=false
// SO previous Question on ihttphandler - http://stackoverflow.com/questions/3359816/can-asp-net-routing-be-used-to-create-clean-urls-for-ashx-ihttphander-handle
// phil haack's Route Debugger http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx
#endregion
public class MyUrlRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
//~/Account/LogOn
//~/Home.aspx - Works fine
//~/home/index.aspx -Works Fine
//http://localhost:57282/home/index/1/2/3 - Works fine
//http://localhost:57282/Account/Register http://localhost:57282/Account/LogOn - Works Fine
string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
//check null for URL
const string defaultcontrollername = "Home";
string[] spliturl = url.Split("//".ToCharArray());
string controllername = String.Empty;
string actionname = "Index";
if (spliturl.Length == 2) //for ~/home.aspx and ~/
{
if (String.IsNullOrEmpty(spliturl[1])) //TODO: http://localhost:57282/ not working - to make it working
{
controllername = defaultcontrollername;
}
else
{
controllername = spliturl[1];
if (controllername.Contains("."))
{
controllername = controllername.Substring(0, controllername.LastIndexOf("."));
}
}
}
else if (spliturl.Length == 3) // For #/home/index.aspx and /home/about
{
controllername = spliturl[1];
actionname = spliturl[2];
if (actionname.Contains("."))
{
actionname = actionname.Substring(0, actionname.LastIndexOf("."));
}
}
else //final block in final case sned it to Home Controller
{
controllername = defaultcontrollername;
}
RouteData rd = new RouteData(this, new MvcRouteHandler());
rd.Values.Add("controller", controllername);
rd.Values.Add("action", actionname);
rd.Values.Add("url", url);
return rd;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
}
in global.asax.cs add below code
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new MyUrlRoute()); // Add before your default Routes
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
working as expected.
possibly you can improve if/elseif code.
Using the Application_BeginRequest
, will allow you to intercept all incoming requests, and allow you to trim the extension. Make sure to ignore requests for your content, such as .css, .js, .jpg, etc. Otherwise those requests will have their extensions trimmed as well.
protected void Application_BeginRequest(object sender, EventArgs e)
{
String originalPath = HttpContext.Current.Request.Url.AbsolutePath;
//Ignore content files (e.g. .css, .js, .jpg, .png, etc.)
if (!Regex.Match(originalPath, "^/[cC]ontent").Success)
{
//Search for a file extension (1 - 5 charaters long)
Match match = Regex.Match(originalPath, "\\.[a-zA-Z0-9]{1,5}$");
if (match.Success)
{
String modifiedPath = String.Format("~{0}", originalPath.Replace(match.Value, String.Empty));
HttpContext.Current.RewritePath(modifiedPath);
}
}
}
If you are using IIS 7, you should look at Dan Atkinson's answer.
I'm using IIS 6, so, in my case, I have the option to install isapi rewrite for IIS 6 or create custom route. I prefer to create my simple custom route class
AndraRoute.cs
// extend Route class,
// so that we can manipulate original RouteData
// by overriding method GetRouteDate
public class AndraRoute : Route
{
// constructor
public AndraRoute(
string url,
RouteValueDictionary defaults,
RouteValueDictionary constraints,
IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
}
// get original RouteData
// check if any route data value has extension '.html' or '.anything'
// remove the extension
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var data = base.GetRouteData(httpContext);
if (data == null) return null;
// from original route data, check
foreach (var pair in data.Values)
{
if (pair.Value.ToString().Contains('.'))
{
var splits = pair.Value.ToString().Split('.');
if (splits[1] == "html" || splits[1] == "anything")
{
data.Values[pair.Key] = splits[0];
}
break;
}
}
return data;
}
}
RouteCollectionExtensionHelper.cs
public static class RouteCollectionExtensionHelper
{
public static Route MapAndraRoute(this RouteCollection routes,
string name, string url, object defaults, object constraints,
string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
}
var route = new AndraRoute(url,
new RouteValueDictionary(defaults),
new RouteValueDictionary(constraints),
new MvcRouteHandler());
if ((namespaces != null) && (namespaces.Length > 0))
{
route.DataTokens = new RouteValueDictionary();
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
}
RegisterRoutes method in Global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("Content/{*pathInfo}");
routes.MapAndraRoute(
"Product",
"product/{id}/{slug}",
new { controller = "product", action = "detail" },
null, null
);
routes.MapAndraRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "home", action = "index", id = UrlParameter.Optional },
null, null
);
}
精彩评论