I have a little problem here. I need to add a trailing slash at the end of each url in the site I'm working on. I defined all the links inside the site to have a trailing slash like so:
<a href="/register/">Register</a>
While this works fine there's still one tiny issue: it's with the generated urls that come from calling RedirectToAction(). For example:
return RedirectToAction("Register", "Users");
Will cause the url to change to /register with no trailing slash. I modified my routing system as so:
routes.MapRoute("register",
"register/",
new {controller = "Users", action = "Register"}
);
Still the required trailing slash doesn't get added automatically!
I looked up this question and this question but these are totally different as I'm not using any url rewriting libraries, i'm just using asp.net mvc routing system.
So what do you say guys?
The RouteCollection Class now has a AppendTrailingSlash boolean which you can set to true for this behavior.
You can create a new Route which overrides the GetVirtualPath
method. In this method you add a trailing slash to the VirtualPath
. Like this:
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData path = base.GetVirtualPath(requestContext, values);
if (path != null)
path.VirtualPath = path.VirtualPath + "/";
return path;
}
For brevity I posted the whole example on CodePaste.net
Now all you have to do is register the routes with routes.MapRouteTrailingSlash()
instead of routes.MapRoute()
.
routes.MapRouteTrailingSlash("register",
"register",
new {controller = "Users", action = "Register"}
);
The route will then add a slash to the path when the GetVirtualPath()
is called. Which RedirectToAction()
will do.
Update: Because the CodePaste link is down, here is the full code:
public class TrailingSlashRoute : Route {
public TrailingSlashRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler) {}
public TrailingSlashRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler) {}
public TrailingSlashRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler) {}
public TrailingSlashRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler) {}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
VirtualPathData path = base.GetVirtualPath(requestContext, values);
if (path != null)
path.VirtualPath = path.VirtualPath + "/";
return path;
}
}
public static class RouteCollectionExtensions {
public static void MapRouteTrailingSlash(this RouteCollection routes, string name, string url, object defaults) {
routes.MapRouteTrailingSlash(name, url, defaults, null);
}
public static void MapRouteTrailingSlash(this RouteCollection routes, string name, string url, object defaults,
object constraints) {
if (routes == null)
throw new ArgumentNullException("routes");
if (url == null)
throw new ArgumentNullException("url");
var route = new TrailingSlashRoute(url, new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints)
};
if (String.IsNullOrEmpty(name))
routes.Add(route);
else
routes.Add(name, route);
}
}
Nice clean solution, codingbug!!
Only problem I ran into was double trailing slashes for the home page in MVC3.
Razor example:
@Html.ActionLink("Home", "Index", "Home")
Linked to:
http://mysite.com//
To fix this I tweaked the GetVirtualPath override:
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData path = base.GetVirtualPath(requestContext, values);
if (path != null && path.VirtualPath != "")
path.VirtualPath = path.VirtualPath + "/";
return path;
}
The above solution by codingbug is very nice. I needed something very similar, but only for my root URL. I know there are possible problems with this, but here is what I did. It seems to work just fine in all of my environments. I think it works too, because it is only our Home page when they first come and do not have the training slash. That is the one case I was trying to avoid. If that is what you want to avoid, this will work for you.
public class HomeController : Controller
{
public ActionResult Index()
{
if (!Request.RawUrl.Contains("Index") && !Request.RawUrl.EndsWith("/"))
return RedirectToAction("Index", "Home", new { Area = "" });
If it turns out I need more than this. I will probably use code that codingbug provided. Until then, I like to keep it simple.
Note: I am counting on Home\Index to be removed from the URL by the routing engine.
I know there are more upvoted answers, but the best voted answer didn't work for my case. Need I say that I found a far easier solution.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/", <-- trailing slash here
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This will act as trailing slash "/" in all the cases.
There are some really good answers but here is some MVC Controller code of how I implement it in a very simple get.
public ActionResult Orders()
{
if (!Request.Path.EndsWith("/"))
{
return RedirectPermanent(Request.Url.ToString() + "/");
}
return View();
}
Hope this helps someone.