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

在ASP.NET Web API進行Controller層級的組態

在ASP.NET Web API要進行組態設置的話,一般會到WebApiConfig.cs進行設置。不過有個好玩的問題,在WebApiConfig.cs進行的組態都是以HttpConfiguration物件在進行操作,MSDN說明表示 HttpServer 執行個體的設定。。也就是說,不管你在何處進行設置,設置之後,它影響是的整個ASP.NET Web API(即HttpServer執行範圍)

Model:TodoItem

以下是範例Model:

 public class TodoItem
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool IsDone { get; set; }
    }
 

然後利用基架含Entity Framework產生TodoItemsConteoller。建置之後,先利用POSTMAN等工具,新增幾筆資料:

api/TodoItems

小寫開頭(CamelCase)

JSON.NET預設序列化依照TodoItem的屬性名稱來輸出,現在有個簡單的需求,希望可以改採小寫開頭(CamelCase)的方式,那麼可以在WebApiConfig.cs這樣設置:

 public static class WebApiConfig
 {
     public static void Register(HttpConfiguration config)
     {
         config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

         // 省略
     }
 }  
 

設置CamelCasePropertyNamesContractResolver之後,解決第一個需求:小寫開頭。

Change CameCase Global

HttpConfiguration的全面性

全域設置影響範圍太大,現在需求改變,希望所需的ApiController類別進行設定即可,那麼我們把腦筋動到ApiController裡面:

 public class TodoItemsController : ApiController
 {
     private TodoContext db = new TodoContext();

     private TodoItemsController()
     {
         // 無法在建構函式修改Configuration設置
         //Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
     }

     // GET: api/TodoItems
     public IQueryable GetTodoItems()
     {
         Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
         return db.TodoItems;
     }

     // 省略
 } 
 

當我們要求過"/api/TodoItems"(執行過Configuration設置之後)再存取其他ApiController,你能發現,其他ApiController也會受影響。就算利用ActionFilter也是一樣,只要執行過HttpConfiguration的設置,影響都會是全面性的。

IControllerConfiguration介面

要在ASP.NET Web API進行Controller層級組態,那麼要使用IControllerConfiguration介面來進行Controller層級的設置,然後套用至所需的ApiController類別。

public interface IControllerConfiguration
{
    void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor);
}
 

介面定義一個Initialize方法,叫用回呼(Callback)以設定此 controllerDescriptor 的各控制器覆寫。其中的HttpControllerSettings是要初始化的控制器設定。

 public sealed class HttpControllerSettings
 {
     public MediaTypeFormatterCollection Formatters { get; }
     public ParameterBindingRulesCollection ParameterBindingRules { get; }
     public ServicesContainer Services { get; }
 }
 
  1. Formatters:取得控制器的 MediaTypeFormatter 執行個體集合。
  2. ParameterBindingRules:取得控制器的參數繫結函數集合。
  3. Services:取得控制器的服務執行個體集合。

實作CamelCasedJsonAttribute

 public class CamelCaseJsonAttribute : Attribute, IControllerConfiguration
 {
     public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
     {
         var jsonformatter = controllerSettings.Formatters.JsonFormatter;
         // 為Controller移除JsonFormatter
         controllerSettings.Formatters.Remove(jsonformatter);
         // 含CamelCasePropertyNamesContractResolver設置的Jsonformatter
         jsonformatter = new JsonMediaTypeFormatter
         {
             SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
         };
         // 為Controller設置新增formatter
         controllerSettings.Formatters.Insert(0, jsonformatter);
     }
 } 
 

注意一下,最後一行,請不要使用 add 來加入,因為以上是使用移除再加入的方式,使用add會改變formatter的順序,設置此屬性的ApiContrller會以XML Formatter為主,JSON Formatter為副。有設置Content-Type是不會有問題,但忘了設置或設置錯誤,程式會直接爆炸了!

這裡對Formatter進行Controller層級的設置,然後設置到我們所需的ApiController:

 [CamelCaseJson]
 public class TodoItemsController : ApiController
 {}
 

執行"api/TodoItems"可以產生符合需求的小寫開頭名稱來輸出。另執行"api/TodoItems2"(Model - TodoItem產生另一個ApiController)則會採用預設名稱輸出。

如果你的Web API已經不提供XMLFormatter的話,也可以這樣寫:

 // 清除所有Formatters
 controllerSettings.Formatters.Clear();
 // 含CamelCasePropertyNamesContractResolver設置的Jsonformatter
 var jsonformatter = controllerSettings.Formatters.JsonFormatter;
 jsonformatter = new JsonMediaTypeFormatter
 {
     SerializerSettings = { ContractResolver = new CamelCasePropertyNamesContractResolver() }
 };
 // 只加入json formatter
 controllerSettings.Formatters.Add(jsonformatter);
 

這樣可以確保不管是誰來要求,都會回應JSON內容。

以上需求來自Sky Chang的真實專案需求 :D

沒有留言:

張貼留言

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