在上一篇中,老周演示了通過實現約定介面的方式自定義控制器的名稱。
至於說自定義操作方法的名稱,就很簡單了,因為有內建的特性類可以用。看看下面的例子。
[Route("[controller]/[action]")] public class StockController : Controller { [ActionName("OutGoing"), HttpGet("{q?}")] public string Sendout(int q) => $"今天發出{q}筆訂單"; }
上述程式碼中,本來操作方法的名稱是「Sendout」,但應用了 ActionName 特性後,就變成「OutGoing」了。存取 /stock/outgoing/12 試試看。
如何?簡單吧。可是有大夥伴會說,那我用實現約定介面的方式能實現嗎?能,擴充套件的是 IActionModelConvention 介面,修改 ActionModel 範例的 ActionName 屬性就可以了。請參考下面程式碼:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class MyActionNameAttribute : Attribute, IActionModelConvention { private readonly string _actionName; public MyActionNameAttribute(string Name) { _actionName = Name; } public void Apply(ActionModel action) { action.ActionName = _actionName; } }
然後,咱們用自己定義的這個特性類替換 ActionName 特性。
[Route("[controller]/[action]")] public class StockController : Controller { [MyActionName("OutGoing"), HttpGet("{q?}")] public string Sendout(int q) => $"今天發出{q}筆訂單"; }
效果是一樣的喲。
------------------------------------------------------------------ 銀河分隔線 ----------------------------------------------------------------
控制器和操作方法的自定義名稱好弄,但,方法引數的名稱就不好弄了。有大夥伴就不樂意了,我直接按思路套程式碼不就行了嗎?擴充套件下 IParameterModelConvention 介面,然後設定 ParameterModel.ParameterName 屬性不就完事了嗎?
是的,夢境總是那麼美好,咱們不妨試試。
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] public class MyParameterAttribute : Attribute, IParameterModelConvention { private readonly string _name; public MyParameterAttribute(string name) { _name = name; } public void Apply(ParameterModel parameter) { parameter.ParameterName = _name; } }
接著,套在控制器的操作方法上。
public class TestController : Controller { [HttpGet("test/get")] public int GetNumber([MyParameter("num")]int xx) => xx * 5; }
試試看,存取 /test/get?num=5。結果……
WTF,這是咋回事呢?不知道夥伴們有沒有看過老周曾寫過模型繫結的水文。其實這裡我們不需要對模型繫結有多深的瞭解,但我們得知道,對於操作方法的引數來說,是存在模型繫結這一過程的。這就導致不能修改一下引數名就完事了,ModelBinder 認的是引數的資料型別,而不是 ApplicationModel 中的資訊。這裡頭牽涉的東西太多了,你無法任性地擴充套件一兩個介面就能完事的。但也不是沒有辦法,不用寫擴充套件,有個現成的特性類也能給引數設定別名。
[HttpGet("test/get")] public int GetNumber([ModelBinder(Name = "num")]int xx) => xx * 5;
使用 ModelBinder特性,然後改一下 Name 屬性就好了。咱們再試試。
怎麼樣,有效果吧。
可你又說了,我要是堅持要通過約定介面來擴充套件,那有法子乎?有,原理一樣,改 ModelBinder 的名字。
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] public class MyParameterAttribute : Attribute, IParameterModelConvention { private readonly string _name; public MyParameterAttribute(string name) { _name = name; } public void Apply(ParameterModel parameter) { // 注意,BindingInfo 屬性可能會為null parameter.BindingInfo ??= new BindingInfo(); // 修改模型名稱 parameter.BindingInfo.BinderModelName = _name; } }
原理就是設定 BindingInfo 類的 BinderModelName 屬性。
再試試看。
[HttpGet("test/get")] public int GetNumber([MyParameter("num")]int xx) => xx * 5;
總算有滿意的結果了。