2012-12-19 72 views
9

我的工作在C#中的JSON界面提供給移動應用程序的ASP.NET Web API項目。我的想法是爲所有請求創建接口,然後僅在Web API代碼中使用這些接口。依賴注入的ASP.NET Web API操作方法參數

我結束了這樣的事情:

public interface IApiObject {} 
public interface IApiResponse<T> : IApiObject where T : IApiObject {} 
public interface IApiRegistrationRequest : IApiObject {} 

我的控制器看起來是這樣的:

public class MyApiController : ApiController 
{ 

    public IApiResponse<IApiObject> Register(IApiRegistrationRequest request) { 
     // do some stuff 
    } 
} 

我的Web API項目還包含這些接口的實現。

我假設Web API項目使用模型綁定,就像MVC項目一樣,所以我創建了一個inheritance aware ModelBinderProvider爲所有IApiObjects提供一個活頁夾,並使用Unity容器爲其實現提供了一個定製模型活頁夾來解析接口。

然而,經過一番更多的調查,我碰到How Web API does parameter binding,並發現網絡API使用格式化的,而不是複雜類型模型粘合劑。鏈接的博客文章建議在我的動作參數上使用ModelBinderAttribute,但該屬性只接受類型作爲參數。然而,我的自定義模型綁定器不包含空構造函數(它需要一個統一容器),所以我需要傳遞它的一個實例。

我能想到的另一種方法是使用依賴注入的格式化。不幸的是,我不熟悉他們,因爲我以前從未使用過他們。

哪一條是正確的路?我該怎麼做?

回答

5

這是我現在想出了和它的作品。

我決定創建一個自定義格式,其不統一調用和使用轉發的解決類型的所有其他操作到其他格式。它看起來像很多代碼,但這只是因爲所有的方法都需要被覆蓋,所以類型總是可以被解析的。

public class UnityFormatter : MediaTypeFormatter 
{ 
    private MediaTypeFormatter formatter; 

    private IUnityContainer container; 

    public UnityFormatter(MediaTypeFormatter formatter, IUnityContainer container) 
    { 
     this.formatter = formatter; 
     this.container = container; 

     foreach (var supportedMediaType in this.formatter.SupportedMediaTypes) 
     { 
      this.SupportedMediaTypes.Add(supportedMediaType); 
     } 

     foreach (var supportedEncoding in this.formatter.SupportedEncodings) 
     { 
      this.SupportedEncodings.Add(supportedEncoding); 
     } 

     foreach (var mediaTypeMapping in this.MediaTypeMappings) 
     { 
      this.MediaTypeMappings.Add(mediaTypeMapping); 
     } 

     this.RequiredMemberSelector = this.formatter.RequiredMemberSelector; 
    } 

    private Type ResolveType(Type type) 
    { 
     return this.container.Registrations.Where(n => n.RegisteredType == type).Select(n => n.MappedToType).FirstOrDefault() ?? type; 
    } 

    public override bool CanReadType(Type type) 
    { 
     return this.formatter.CanReadType(this.ResolveType(type)); 
    } 

    public override bool CanWriteType(Type type) 
    { 
     return this.formatter.CanWriteType(this.ResolveType(type)); 
    } 

    public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType) 
    { 
     return this.formatter.GetPerRequestFormatterInstance(this.ResolveType(type), request, mediaType); 
    } 

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) 
    { 
     return this.formatter.ReadFromStreamAsync(this.ResolveType(type), readStream, content, formatterLogger); 
    } 

    public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType) 
    { 
     this.formatter.SetDefaultContentHeaders(this.ResolveType(type), headers, mediaType); 
    } 

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) 
    { 
     return this.formatter.WriteToStreamAsync(this.ResolveType(type), value, writeStream, content, transportContext); 
    } 
} 

最後,註冊我們的應用程序配置(Global.asax中的Application_Start)自定義格式。我選擇用我自定義的實例替換所有當前的格式化程序,所以我得到所有數據類型的反射。

// set up unity container, register all types 
UnityContainer container = new UnityContainer(); 
container.RegisterType<IApiRegistrationRequest, ApiRegistrationRequest>(); 

// save existing formatters and remove them from the config 
List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>(GlobalConfiguration.Configuration.Formatters); 
GlobalConfiguration.Configuration.Formatters.Clear(); 

// create an instance of our custom formatter for each existing formatter 
foreach (MediaTypeFormatter formatter in formatters) 
{ 
    GlobalConfiguration.Configuration.Formatters.Add(new UnityFormatter(formatter, container)); 
}