【ASP.NET Core】MVC控制器的各種自定義:修改引數的名稱

2022-11-19 12:00:51

在上一篇中,老周演示了通過實現約定介面的方式自定義控制器的名稱。

至於說自定義操作方法的名稱,就很簡單了,因為有內建的特性類可以用。看看下面的例子。

    [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;

 

總算有滿意的結果了。