2012-12-19 10 views
9

Sto lavorando a un progetto API Web ASP.NET in C# per un'interfaccia JSON a un'applicazione mobile. La mia idea era di creare interfacce per tutte le richieste e quindi utilizzare solo queste interfacce nel codice API Web.Iniezione di dipendenza per parametri del metodo di azione dell'API Web ASP.NET

ho finito con qualcosa di simile:

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

mio regolatore di simile a questa:

public class MyApiController : ApiController 
{ 

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

Il mio progetto API Web contiene anche implementazioni di queste interfacce.

Supponevo che i progetti API Web utilizzassero l'associazione modello come fanno i progetti MVC, quindi ho creato un inheritance aware ModelBinderProvider per fornire un raccoglitore per tutti gli IApiObjects e un raccoglitore modello personalizzato utilizzando un contenitore Unity per risolvere le interfacce con le loro implementazioni.

Tuttavia, dopo ulteriori indagini, ho trovato How Web API does parameter binding e ho scoperto che l'API Web utilizza i formattori anziché i raccoglitori di modelli per tipi complessi. Il post del blog collegato consiglia di utilizzare ModelBinderAttribute sui miei parametri di azione, ma tale attributo accetta solo un tipo come parametro. Tuttavia, il mio modello personalizzato non contiene un costruttore vuoto (ha bisogno di un contenitore di unità), quindi dovrei passare un'istanza di esso.

L'altro modo in cui riesco a pensare è utilizzare l'iniezione di dipendenza per i formattatori. Sfortunatamente, non li conosco come non li ho mai usati prima.

Qual è la giusta direzione? E come lo faccio?

risposta

5

Questo è ciò che mi si avvicinò con la società e funziona.

Ho deciso di creare un formattatore personalizzato che fa l'unità chiama e inoltra tutte le ulteriori operazioni a un altro formattatore utilizzando il tipo risolto. Sembra un sacco di codice, ma questo è solo perché tutti i metodi devono essere sovrascritti in modo che il tipo possa sempre essere risolto.

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); 
    } 
} 

Infine, registrare il nostro formattatore personalizzato nella configurazione dell'applicazione (Global.asax Application_Start). Ho scelto di sostituire tutti i formattatori correnti con un'istanza di mia personalizzata, quindi ottengo la riflessione per tutti i tipi di dati.

// 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)); 
} 
Problemi correlati