2012-10-02 7 views
5

poner a prueba API web para la carga de archivos, tiene un modelo simple vista como esta:¿La carpeta del modelo de API web no funciona con HttpPostedFileBase?

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

utilizado en el método:

[HttpPost] 
public void Create(TestModel model) 

Cuando intento publicar un/form form-data codificada multiparte a la acción, recibo esta excepción:

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) 

esto funciona con el ligante modelo MVC por defecto, pero al parecer no con la API de web. Se encontraron algunas menciones de que no se puede usar un modelo de vista al cargar un archivo y separar los datos en dos llamadas. Eso no funciona para mí, ya que necesito que se publiquen los otros campos para hacer algo con el archivo cargado. ¿Hay alguna manera de lograr esto?

+0

Necesita escribir un 'MediaTypeFormatter' personalizado para que funcione. Como experimentó "multipart/form-data" no es compatible de forma inmediata. Puede comenzar [aquí] (http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/) – nemesv

Respuesta

3

Véase mi respuesta original https://stackoverflow.com/a/12603828/1171321

combinan Básicamente mi método en mi blog y el TryValidateProperty() sugerencia para mantener las anotaciones de validación del modelo.

Edit: Seguí adelante y desarrollé una mejora de código para mi código en la publicación del blog. Voy a publicar este código actualizado en breve. Aquí hay un ejemplo simple que valida cada propiedad y le da acceso a una matriz de resultados. Solo una muestra de un enfoque

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

Puede escribir un MediaTypeFormatter personalizado para facilitar su escenario o puede extraer los datos de la solicitud a mano utilizando la colección MultipartFormDataStreamProvider.FormData.AllKeys. De esta manera puede publicar tanto el archivo (s) como campos adicionales en una solicitud.

Un buen tutorial por Mike Wasson está disponible aquí: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2

+7

Luego de leer el artículo, veo cómo se puede hacer esto . Sin embargo, a menos que sea un malentendido, todavía no hay forma de usar el encuadernador de modelo (y, por lo tanto, todas las anotaciones de datos que lo acompañan). Entonces, ¿ahora tenemos que validar toda la entrada manualmente? ¿Sabe por casualidad por qué este cambio fue necesario en la carpeta de modelos? Con bastante efectividad rompe la noción de poder usar modelos existentes para proyectos API, y parece una dirección realmente extraña de tomar. – heyseuss

Cuestiones relacionadas