ASP.NET Core中使用表达式树创建URL
當(dāng)我們?cè)贏SP.NET Core中生成一個(gè)action的url會(huì)這樣寫:
| var?url=_urlHelper.Action("Index", "Home"); |
這樣的寫法存在的問題在于我們傳遞了兩個(gè)字符串類型的參數(shù),而我們又無法避免對(duì)action和controller做重命名操作, 例如將index重命名為default, 你無法通過IDE在重命名action的過程中,將
| _urlHelper.Action("Index", "Home"); |
重構(gòu)為
| UrlHelper.Action("Default", "Home"); |
所以我們的目標(biāo)是:設(shè)計(jì)出具有靜態(tài)檢查的API,讓IDE提示出這個(gè)錯(cuò)誤來,甚至是重命名時(shí)直接把相關(guān)代碼都能重命名。
目標(biāo)
設(shè)計(jì)出類似兩組API:
| var?url = _urlHelper.Action((HomeController c) => c.Index());//期待輸出 /home/indexvar?link = _urlHelper.Link((ProductController c) => c.Details(10));//期待輸出 http://locahost/product/details/10 |
設(shè)計(jì)API
根據(jù)上面的需求,定義兩組API:
| public?static?string?Action<TController>(this?IUrlHelper helper,Expression<Action<TController>> action)where?TController : Controller{//實(shí)現(xiàn)}public?static?string?Link<TController>(this?IUrlHelper helper,Expression<Action<TController>> action,string?protocal = null, string?host = null)where?TController : Controller{//實(shí)現(xiàn)} |
實(shí)現(xiàn)API
我們實(shí)際上最終還是要依賴ASP.NET Core提供的API:
| var?link = helper.Action(action: actionName, controller:controllerName, values: routes); |
所以問題變成了如何根據(jù)(HomeController c) => c.Index()這樣的表達(dá)式來解析出actionName, ControllerName以及routeValues。
1. 解析ControllerName
解析ControllerName比較簡單粗暴,因?yàn)槲覀円呀?jīng)從表達(dá)式樹中得到了HomeController這個(gè)類型,直接取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
由于表達(dá)式(HomeController c) => c.Index()是一個(gè)MethodCallExpression類型,而Action的名字就是方法名:
| private?static?MethodCallExpressionGetMethodCallExpression<TController>(Expression<Action<TController>> 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
上面兩步已經(jīng)解析出了ControllerName和ActionName,也就是說通過上面的分析已經(jīng)能完成下面的調(diào)用:
| var?action = helper.Action(action: "index", controller: "home", values: null);//等價(jià)于var?url = _urlHelper.Action((HomeController c) => c.Index());//輸出 /home/index |
但是考慮下面的Action:
| [HttpGet,Route("product/{id}")]public?IActionResult Details(int?id){//...} |
這個(gè)Action期待傳入一個(gè)int類型的id,也就是說你要通過這樣的方式來生成url:
| var?action = helper.Action(action: "details", controller:"product", values: new?{ id = 10 }); |
所以要想讓我們的API正常工作,還需要生成一個(gè)object類型:new { id = 10 }。而這個(gè)object類型里面的屬性正好可以來自于表達(dá)式樹的方法調(diào)用參數(shù):
| var?action = _urlHelper.Action((ProductController c) => c.Details(10)); |
要想生成這個(gè)匿名對(duì)象,需要遍歷方法調(diào)用表達(dá)式的所有參數(shù),分別解析出屬性名,例如id; 以及值,例如10。最后再把解析出來的參數(shù)字典生成為dynamic類型的對(duì)象:
如何解析表達(dá)式樹請(qǐng)查看expression-trees。
| public?class?RouteValueExtractor{public?static?object?GetRouteValues(MethodCallExpression call){var?routes = new?Dictionary<string, object>();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<string, object> dictionary){var?expandoObj = new?ExpandoObject();var?expandoObjCollection = (ICollection<KeyValuePair<string, object>>) expandoObj;foreach?(var?keyValuePair in?dictionary){expandoObjCollection.Add(keyValuePair);}dynamic eoDynamic = expandoObj;return?eoDynamic;}} |
一個(gè)完整的API實(shí)現(xiàn):
| public?static?string?Action<TController>(this?IUrlHelper helper,Expression<Action<TController>> 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;} |
原文地址:?https://www.cnblogs.com/xiandnc/p/9746274.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core中使用表达式树创建URL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 2017 与
- 下一篇: 在你的andorid设备上运行netco