表达式树 php,ASP.NET Core中使用表达式树创建URL

当我们在ASP.NET Core中生成一个action的url会这样写:

var url=_urlHelper.Action("Index", "Home");

这样的写法存在的问题在于我们传递了两个字符串类型的参数,而我们又无法避免对action和controller做重命名操作, 例如将index重命名为default, 你无法通过IDE在重命名action的过程中,将_urlHelper.Action("Index", "Home");

重构为UrlHelper.Action("Default", "Home");

所以我们的目标是:设计出具有静态检查的API,让IDE提示出这个错误来,甚至是重命名时直接把相关代码都能重命名。

目标

设计出类似两组API:var url = _urlHelper.Action((HomeController c) => c.Index());

//期待输出 /home/index

var link = _urlHelper.Link((ProductController c) => c.Details(10));

//期待输出 http://locahost/product/details/10

设计API

根据上面的需求,定义两组API:public static string Action(this IUrlHelper helper,

Expression> action)

where TController : Controller

{

//实现

}

public static string Link(this IUrlHelper helper,

Expression> action,

string protocal = null, string host = null)

where TController : Controller

{

//实现

}

实现API

我们实际上最终还是要依赖ASP.NET Core提供的API:var link = helper.Action(action: actionName, controller:

controllerName, values: routes);

所以问题变成了如何根据(HomeController c) => c.Index()这样的表达式来解析出actionName, ControllerName以及routeValues。

1. 解析ControllerName

解析ControllerName比较简单粗暴,因为我们已经从表达式树中得到了HomeController这个类型,直接取Home字符串即可:private static string GetControllerName(Type controllerType)

{

var controllerName = controllerType.Name.EndsWith("Controller")

? controllerType.Name.Substring(0,

controllerType.Name.Length - "Controller".Length)

: controllerType.Name;

return controllerName;

}

2. 解析ActionName

由于表达式(HomeController c) => c.Index()是一个MethodCallExpression类型,而Action的名字就是方法名:private static MethodCallExpression

GetMethodCallExpression(

Expression> actionSelector)

{

var call = actionSelector.Body as MethodCallExpression;

if (call == null)

{

throw new ArgumentException("You must call a method on " +

typeof(TController).Name, "actionSelector");

}

return call;

}

var methodCallExpression = GetMethodCallExpression(action);

var actionName = methodCallExpression.Method.Name;

3. 解析RouteValues

上面两步已经解析出了ControllerName和ActionName,也就是说通过上面的分析已经能完成下面的调用:var action = helper.Action(action: "index", controller: "home", values: null);

//等价于

var url = _urlHelper.Action((HomeController c) => c.Index());

//输出 /home/index

但是考虑下面的Action:[HttpGet,Route("product/{id}")]

public IActionResult Details(int id)

{

//...

}

这个Action期待传入一个int类型的id,也就是说你要通过这样的方式来生成url:var action = helper.Action(action: "details", controller:

"product", values: new { id = 10 });

所以要想让我们的API正常工作,还需要生成一个object类型:new { id = 10 }。而这个object类型里面的属性正好可以来自于表达式树的方法调用参数:var action = _urlHelper.Action((ProductController c) => c.Details(10));

要想生成这个匿名对象,需要遍历方法调用表达式的所有参数,分别解析出属性名,例如id; 以及值,例如10。最后再把解析出来的参数字典生成为dynamic类型的对象:

如何解析表达式树请查看expression-trees。public class RouteValueExtractor

{

public static object GetRouteValues(MethodCallExpression call)

{

var routes = new Dictionary();

var parameters = call.Method.GetParameters();

var pairs = call.Arguments.Select((a, i) => new

{

Argument = a,

ParamName = parameters[i].Name

});

foreach (var item in pairs)

{

string name = item.ParamName;

object value = GetValue(item.Argument);

if (value != null)

{

var valueType = value.GetType();

if (valueType.IsValueType)

{

routes.Add(name, value);

}

else

{

throw new NotSupportedException("Unsupported parameter type {0}");

}

}

}

return DictionaryToObject(routes);

}

private static object GetValue(Expression expression)

{

if (expression.NodeType == ExpressionType.Constant)

{

return ((ConstantExpression) expression).Value;

}

throw new NotSupportedException("Unsupported parameter expression");

}

private static dynamic DictionaryToObject(IDictionary dictionary)

{

var expandoObj = new ExpandoObject();

var expandoObjCollection = (ICollection>) expandoObj;

foreach (var keyValuePair in dictionary)

{

expandoObjCollection.Add(keyValuePair);

}

dynamic eoDynamic = expandoObj;

return eoDynamic;

}

}

一个完整的API实现:public static string Action(this IUrlHelper helper,

Expression> action)

where TController : Controller

{

var controllerName = GetControllerName(typeof(TController));

var methodCallExpression = GetMethodCallExpression(action);

var actionName = methodCallExpression.Method.Name;

var routes = RouteValueExtractor.GetRouteValues(methodCallExpression);

var link = helper.Action(action: actionName, controller:

controllerName, values: routes);

return link;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值