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

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

實作IControllerConfiguration介面

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

 public class JsonOnlyAttribute : Attribute, IControllerConfiguration
 {
     public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
     {
         controllerSettings.Formatters.Clear();
         controllerSettings.Formatters.Add(new JsonMediaTypeFormatter());
     }
 }  
 

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

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

態動Controller層級組態

一、新增一個延伸方法:

 public static class HttpConfigurationExtensions
 {
     /// <summary>
     /// 建立新HttpControllerSettings實體,並套用提供組態使用者的變更。
     /// </summary>
     /// <param name="configuration">HttpConfiguration</param>
     /// <param name="settings">HttpControllerSettings</param>
     public static HttpConfiguration Copy(this HttpConfiguration configuration, Action<HttpControllerSettings> settings)
     {
         var controllerSettings = new HttpControllerSettings(configuration);
         settings(controllerSettings);

         // 呼叫HttpConfiguration私有建構子(NonPublic)以建立調整用的HttpControllerSettings組態物件
         var constructor = typeof(HttpConfiguration).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, 
                                                                    null, 
                                                                    new[] { typeof(HttpConfiguration), 
                                                                    typeof(HttpControllerSettings) }, 
                                                                    null);
         var instance = (HttpConfiguration)constructor.Invoke(new object[] { configuration, controllerSettings });

         return instance;
     }
 }  
 

二、儲存組態設置

 /// <summary>
 /// 儲存Controller組態
 /// </summary>
 public class ControllerConfig
 {
     public static Dictionary<Type, Action<HttpControllerSettings>> Map = new Dictionary<Type, Action<HttpControllerSettings>>();
 } 
 

三、由自訂IHttpControllerActivator呼叫HttpConfigurationExtensions方法

 public class PerControllerConfigActivator : IHttpControllerActivator
 {
     private static readonly DefaultHttpControllerActivator Default = new DefaultHttpControllerActivator();
     private readonly ConcurrentDictionary"Type, HttpConfiguration> _cache = new ConcurrentDictionary<Type, HttpConfiguration>();

     /// <summary>
     /// 建立 System.Web.Http.Controllers.IHttpController 物件。
     /// </summary>
     /// <param name="request">訊息要求。</param>
     /// <param name="controllerDescriptor">HTTP 控制器描述元。</param>
     /// <param name="controllerType">控制器的型別。</param>
     /// <returns>IHttpController 物件</returns>
     public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
     {
         HttpConfiguration controllerConfig;

         // 檢查每一個當下的Controller,是否有我們要組態的Controller型別
         // TryGetValue方法:嘗試從 ConcurrentDictionary<TKey, TValue> 取得與指定之索引鍵相關聯的值。
         if (_cache.TryGetValue(controllerType, out controllerConfig))
         {
             controllerDescriptor.Configuration = controllerConfig;
         }
         else if (ControllerConfig.Map.ContainsKey(controllerType))
         {
             // 呼叫 HttpConfigurationExtensions.Copy方法
             controllerDescriptor.Configuration = controllerDescriptor.Configuration.Copy(ControllerConfig.Map[controllerType]);
             // 為此Controller型別加入組態
             // TryAdd方法:如果它不存在於字典中,加入新的索引鍵加入字典
             _cache.TryAdd(controllerType, controllerDescriptor.Configuration);
         }

         // 建立新的IHttpcontroller
         var result = Default.Create(request, controllerDescriptor, controllerType);
         return result;
     }
 }  
 

四、WebApiConfig進行動態組態

 // 自訂IHttpControllerActivator,叫呼新HttpConfigurationExtensions方法,
 config.Services.Replace(typeof(IHttpControllerActivator), new PerControllerConfigActivator());

 // 動態加入Controller層級設置
 ControllerConfig.Map.Add(typeof(TodoItems2Controller), settings =>
 {
     settings.Formatters.Clear();
     var jsonformatter = new JsonMediaTypeFormatter
     {
         SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
     };
     settings.Formatters.Add(jsonformatter);
 });

 ControllerConfig.Map.Add(typeof(TodoItemsController), settings =>
 {
     settings.Formatters.Clear();
     settings.Formatters.Add(new XmlMediaTypeFormatter());
 });
 

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

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

沒有留言:

張貼留言

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