Skip to content

Instantly share code, notes, and snippets.

@jtheisen
Created November 3, 2014 16:10
Show Gist options
  • Save jtheisen/c78a8b08f0913b756b2a to your computer and use it in GitHub Desktop.
Save jtheisen/c78a8b08f0913b756b2a to your computer and use it in GitHub Desktop.
Name- and type-safe ASP.NET MVC url creation
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();
}
}
}
}
@jtheisen
Copy link
Author

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment