Created
November 3, 2014 16:10
-
-
Save jtheisen/c78a8b08f0913b756b2a to your computer and use it in GitHub Desktop.
Name- and type-safe ASP.NET MVC url creation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Web.Mvc; | |
using System.Web.Routing; | |
namespace MonkeyBusters.Web.Mvc | |
{ | |
public static class SafeMvcUrls | |
{ | |
public static RedirectToRouteResult RedirectToActionPermanent<C>(this Controller c, Expression<Func<C, ActionResult>> call) | |
where C : IController | |
{ | |
return c.RedirectToAction(call, true); | |
} | |
public static RedirectToRouteResult RedirectToAction<C>(this Controller c, Expression<Func<C, ActionResult>> call, Boolean permenant = false) | |
where C : IController | |
{ | |
var mvcAction = Get<C>(call); | |
mvcAction.Parameter.Add("controller", GetControllerName(typeof(C))); | |
mvcAction.Parameter.Add("action", mvcAction.Action.Name); | |
return new RedirectToRouteResult(mvcAction.Parameter); | |
} | |
public static String Action<C>(this UrlHelper url, Expression<Func<C, ActionResult>> call) | |
where C : IController | |
{ | |
var mvcAction = Get<C>(call); | |
return url.Action(mvcAction.Action.Name, GetControllerName(mvcAction.Controller), mvcAction.Parameter); | |
} | |
public static String Action<C>(this UrlHelper url, Expression<Func<C, ActionResult>> call, String protocol) | |
where C : IController | |
{ | |
var mvcAction = Get<C>(call); | |
return url.Action(mvcAction.Action.Name, GetControllerName(mvcAction.Controller), mvcAction.Parameter, protocol); | |
} | |
public static String Action<C>(this UrlHelper url, Expression<Func<C, ActionResult>> call, String protocol, String hostname) | |
where C : IController | |
{ | |
var mvcAction = Get<C>(call); | |
return url.Action(mvcAction.Action.Name, GetControllerName(mvcAction.Controller), mvcAction.Parameter, protocol, hostname); | |
} | |
static String GetControllerName(Type controllerType) | |
{ | |
var typeName = controllerType.Name; | |
if (typeName.ToLower().EndsWith("controller")) | |
{ | |
return typeName.Substring(0, typeName.Length - "controller".Length); | |
} | |
else | |
{ | |
return typeName; | |
} | |
} | |
class MvcAction | |
{ | |
public Type Controller { get; set; } | |
public MethodInfo Action { get; set; } | |
public RouteValueDictionary Parameter { get; set; } | |
} | |
static MvcAction Get<C>(Expression<Func<C, ActionResult>> x) | |
where C : IController | |
{ | |
var root = x.Body as MethodCallExpression; | |
if (root == null) throw new ArgumentException("Call expression expected."); | |
var method = root.Method; | |
var parameters = method.GetParameters(); | |
var arguments = root.Arguments; | |
var routeValues = new RouteValueDictionary(); | |
for (var i = 0; i < parameters.Length; ++i) | |
{ | |
try | |
{ | |
routeValues[parameters[i].Name] = Evaluate(arguments[i]); | |
} | |
catch (Exception ex) | |
{ | |
throw new Exception(String.Format("Failed to evaluate argument #{0} of an mvc action call while creating a url, look at the inner exceptions.", i), ex); | |
} | |
} | |
return new MvcAction() | |
{ | |
Controller = typeof(C), | |
Action = root.Method, | |
Parameter = routeValues | |
}; | |
} | |
static Object Evaluate(Expression e) | |
{ | |
if (e is ConstantExpression) | |
{ | |
return (e as ConstantExpression).Value; | |
} | |
else | |
{ | |
return Expression.Lambda(e).Compile().DynamicInvoke(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Mart-Bogdan Just saw your comment almost a year later...
It is a library now, and a nuget package. See my
safe-mvc-urls
project on GitHub.