在ASP.NET Web API 2進行動態的ApiController層級組態

前一篇「在ASP.NET WEB AP 2進行APICONTROLLER層級的組態」是透過屬性(Attribute)設置方式來為ApiControler加入靜態設置。它無法在執行期間改變。

實作IControllerConfiguration介面

方法前一篇已經介紹過了:

1public class JsonOnlyAttribute : Attribute, IControllerConfiguration
2{
3    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
4    {
5        controllerSettings.Formatters.Clear();
6        controllerSettings.Formatters.Add(new JsonMediaTypeFormatter());
7    }
8

以上是僅提供JSON Formatter的範例,這樣的好處是不用整個ASP.NET Web API 2專案都完全關閉XML Formatter,需要關閉時才關閉。

如果你是希望動態進行Controller層級組態的話,其實前一篇的參考文件有留下一篇MVP Filip的參考文章,以下簡述Filip文章以瞭解如何進行動態Controller組態。

態動Controller層級組態

一、新增一個延伸方法:

01public static class HttpConfigurationExtensions
02{
03    /// <summary>
04    /// 建立新HttpControllerSettings實體,並套用提供組態使用者的變更。
05    /// </summary>
06    /// <param name="configuration">HttpConfiguration</param>
07    /// <param name="settings">HttpControllerSettings</param>
08    public static HttpConfiguration Copy(this HttpConfiguration configuration, Action<HttpControllerSettings> settings)
09    {
10        var controllerSettings = new HttpControllerSettings(configuration);
11        settings(controllerSettings);
12 
13        // 呼叫HttpConfiguration私有建構子(NonPublic)以建立調整用的HttpControllerSettings組態物件
14        var constructor = typeof(HttpConfiguration).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
15                                                                   null,
16                                                                   new[] { typeof(HttpConfiguration),
17                                                                   typeof(HttpControllerSettings) },
18                                                                   null);
19        var instance = (HttpConfiguration)constructor.Invoke(new object[] { configuration, controllerSettings });
20 
21        return instance;
22    }
23

二、儲存組態設置

1/// <summary>
2/// 儲存Controller組態
3/// </summary>
4public class ControllerConfig
5{
6    public static Dictionary<Type, Action<HttpControllerSettings>> Map = new Dictionary<Type, Action<HttpControllerSettings>>();
7}

三、由自訂IHttpControllerActivator呼叫HttpConfigurationExtensions方法

01public class PerControllerConfigActivator : IHttpControllerActivator
02{
03    private static readonly DefaultHttpControllerActivator Default = new DefaultHttpControllerActivator();
04    private readonly ConcurrentDictionary"Type, HttpConfiguration> _cache = new ConcurrentDictionary<Type, HttpConfiguration>();
05 
06    /// <summary>
07    /// 建立 System.Web.Http.Controllers.IHttpController 物件。
08    /// </summary>
09    /// <param name="request">訊息要求。</param>
10    /// <param name="controllerDescriptor">HTTP 控制器描述元。</param>
11    /// <param name="controllerType">控制器的型別。</param>
12    /// <returns>IHttpController 物件</returns>
13    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
14    {
15        HttpConfiguration controllerConfig;
16 
17        // 檢查每一個當下的Controller,是否有我們要組態的Controller型別
18        // TryGetValue方法:嘗試從 ConcurrentDictionary<TKey, TValue> 取得與指定之索引鍵相關聯的值。
19        if (_cache.TryGetValue(controllerType, out controllerConfig))
20        {
21            controllerDescriptor.Configuration = controllerConfig;
22        }
23        else if (ControllerConfig.Map.ContainsKey(controllerType))
24        {
25            // 呼叫 HttpConfigurationExtensions.Copy方法
26            controllerDescriptor.Configuration = controllerDescriptor.Configuration.Copy(ControllerConfig.Map[controllerType]);
27            // 為此Controller型別加入組態
28            // TryAdd方法:如果它不存在於字典中,加入新的索引鍵加入字典
29            _cache.TryAdd(controllerType, controllerDescriptor.Configuration);
30        }
31 
32        // 建立新的IHttpcontroller
33        var result = Default.Create(request, controllerDescriptor, controllerType);
34        return result;
35    }
36

四、WebApiConfig進行動態組態

01// 自訂IHttpControllerActivator,叫呼新HttpConfigurationExtensions方法,
02config.Services.Replace(typeof(IHttpControllerActivator), new PerControllerConfigActivator());
03 
04// 動態加入Controller層級設置
05ControllerConfig.Map.Add(typeof(TodoItems2Controller), settings =>
06{
07    settings.Formatters.Clear();
08    var jsonformatter = new JsonMediaTypeFormatter
09    {
10        SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
11    };
12    settings.Formatters.Add(jsonformatter);
13});
14 
15ControllerConfig.Map.Add(typeof(TodoItemsController), settings =>
16{
17    settings.Formatters.Clear();
18    settings.Formatters.Add(new XmlMediaTypeFormatter());
19});

註解我都寫在程式碼之中了。注意,態動進行Controller層級組態與實作IControllerConfiguration介面的屬性設置方式無法併存,原因也很簡單,因為我們替換了自行實作的IHttpControllerActivator,它並不懂的如何解讀實作IControllerConfiguration介面的屬性。

如果只有少數Controller層級組態,那麼ASP.NET Web API 2的IControllerConfiguration介面是個不錯的方式。如果有大量的Controller層級組態需求,那麼可以考慮Filip所提供的解決方案。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。