2012-10-02 14 views
5

testare API Web per il caricamento dei file, hanno una visione semplice modello come questo:Il raccoglitore modello di API Web non funziona con HttpPostedFileBase?

public class TestModel { 
    public string UserId {get;set;} 
    public HttpPostedFileBase ImageFile {get;set;} 
} 

utilizzato nel metodo:

[HttpPost] 
public void Create(TestModel model) 

Quando tento di inviare un multipart/form form-data codificati all'azione, ricevo questa eccezione:

System.InvalidOperationException: No MediaTypeFormatter is available to read an object of type 'TestModel' from content with media type 'multipart/form-data'. 
    at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) 
    at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) 
    at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) 
    at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) 
    at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(HttpParameterBinding parameterBinder) 
    at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() 
    at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken) 

questo funziona con il modello MVC legante di default, ma a quanto pare non con API di Web. È stato rilevato che non è possibile utilizzare un modello di visualizzazione durante il caricamento di un file e semplicemente separare i dati in due chiamate. Ciò non funziona per me, dal momento che ho bisogno che gli altri campi vengano pubblicati per fare effettivamente qualcosa con il file caricato. C'è un modo per realizzare questo?

+0

È necessario scrivere un 'MediaTypeFormatter' personalizzato per farlo funzionare. Come hai riscontrato "multipart/form-data" non è supportato immediatamente. Puoi iniziare [qui] (http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/) – nemesv

risposta

3

Vedere la mia risposta originale https://stackoverflow.com/a/12603828/1171321

In sostanza si combinano il mio metodo nel mio post sul blog e la TryValidateProperty() suggerimento per mantenere le annotazioni di validazione del modello.

Modifica: Sono andato avanti e ho elaborato un miglioramento del codice per il mio codice nel post del blog. Ho intenzione di pubblicare questo codice aggiornato a breve. Ecco un semplice esempio che convalida ogni proprietà e ti dà accesso a una matrice di risultati. Solo un esempio di un approccio

public class FileUpload<T> 
{ 
    private readonly string _RawValue; 

    public T Value { get; set; } 
    public string FileName { get; set; } 
    public string MediaType { get; set; } 
    public byte[] Buffer { get; set; } 

    public List<ValidationResult> ValidationResults = new List<ValidationResult>(); 

    public FileUpload(byte[] buffer, string mediaType, string fileName, string value) 
    { 
     Buffer = buffer; 
     MediaType = mediaType; 
     FileName = fileName.Replace("\"",""); 
     _RawValue = value; 

     Value = JsonConvert.DeserializeObject<T>(_RawValue); 

     foreach (PropertyInfo Property in Value.GetType().GetProperties()) 
     { 
      var Results = new List<ValidationResult>(); 
      Validator.TryValidateProperty(Property.GetValue(Value), 
              new ValidationContext(Value) {MemberName = Property.Name}, Results); 
      ValidationResults.AddRange(Results); 
     } 
    } 

    public void Save(string path, int userId) 
    { 
     if (!Directory.Exists(path)) 
     { 
      Directory.CreateDirectory(path); 
     } 
     var SafeFileName = Md5Hash.GetSaltedFileName(userId,FileName); 
     var NewPath = Path.Combine(path, SafeFileName); 
     if (File.Exists(NewPath)) 
     { 
      File.Delete(NewPath); 
     } 

     File.WriteAllBytes(NewPath, Buffer); 

     var Property = Value.GetType().GetProperty("FileName"); 
     Property.SetValue(Value, SafeFileName, null); 
    } 
} 
5

È possibile scrivere un numero personalizzato MediaTypeFormatter per facilitare lo scenario oppure è possibile estrarre i dati dalla richiesta manualmente utilizzando la raccolta MultipartFormDataStreamProvider.FormData.AllKeys. In questo modo puoi pubblicare sia i file che i campi aggiuntivi in ​​una richiesta.

Un buon tutorial da Mike Wasson è disponibile qui: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2

+7

Quindi, dopo aver letto l'articolo, vedo come questo può essere fatto . Tuttavia, a meno che non stia fraintendendo, non c'è ancora modo di usare il modello di raccoglitore (e quindi tutte le varie annotazioni di dati che lo accompagnano). Quindi ora dobbiamo convalidare tutto l'input manualmente? Sapete per caso perché questo cambiamento è stato necessario nel modello di legatura? Rompe abbastanza efficacemente la nozione di poter usare i modelli esistenti per i progetti API e sembra una direzione davvero strana da prendere. – heyseuss

Problemi correlati