2016-12-28 30 views
11

Làm cách nào tôi có thể tải lên danh sách tệp (hình ảnh) và dữ liệu json lên bộ điều khiển API ASP.NET Web Web bằng cách sử dụng tải lên nhiều phần?Tải lên tệp và JSON trong ASP.NET Core Web API

tôi có thể nhận được thành công một danh sách các tập tin, tải lên với multipart/form-data kiểu nội dung như thế:

public async Task<IActionResult> Upload(IList<IFormFile> files) 

Và dĩ nhiên là tôi thành công có thể nhận được HTTP yêu cầu cơ thể định dạng để đối tượng của tôi sử dụng mặc định JSON định dạng như thế:

public void Post([FromBody]SomeObject value) 

Nhưng làm cách nào để kết hợp hai thứ này trong một thao tác điều khiển đơn? Làm thế nào tôi có thể tải lên cả hai hình ảnh và dữ liệu JSON và có chúng liên kết với các đối tượng của tôi?

+0

tập tin có nghĩa là để được gửi với 'multipart/form-dữ liệu'. JSON có nghĩa là được gửi với 'application/json'. Bạn chỉ có thể gửi một loại. Vì vậy, không có cách nào làm sạch việc này. – Fred

Trả lời

5

Dường như không có cách nào được xây dựng để làm những gì tôi muốn. Vì vậy, tôi đã kết thúc viết ModelBinder của riêng mình để xử lý tình huống này. Tôi không tìm thấy bất kỳ tài liệu chính thức nào về ràng buộc mô hình tùy chỉnh nhưng tôi đã sử dụng this post làm tham chiếu.

Tùy chỉnh ModelBinder sẽ tìm kiếm thuộc tính được trang trí với FromJson thuộc tính và chuỗi deserialize xuất phát từ yêu cầu nhiều phần sang JSON. Tôi quấn mô hình của tôi bên trong một lớp khác (wrapper) có mô hình và thuộc tính IFormFile.

IJsonAttribute.cs:

public interface IJsonAttribute 
{ 
    object TryConvert(string modelValue, Type targertType, out bool success); 
} 

FromJsonAttribute.cs:

using Newtonsoft.Json; 
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public class FromJsonAttribute : Attribute, IJsonAttribute 
{ 
    public object TryConvert(string modelValue, Type targetType, out bool success) 
    { 
     var value = JsonConvert.DeserializeObject(modelValue, targetType); 
     success = value != null; 
     return value; 
    } 
} 

JsonModelBinderProvider.cs:

public class JsonModelBinderProvider : IModelBinderProvider 
{ 
    public IModelBinder GetBinder(ModelBinderProviderContext context) 
    { 
     if (context == null) throw new ArgumentNullException(nameof(context)); 

     if (context.Metadata.IsComplexType) 
     { 
      var propName = context.Metadata.PropertyName; 
      var propInfo = context.Metadata.ContainerType?.GetProperty(propName); 
      if(propName == null || propInfo == null) 
       return null; 
      // Look for FromJson attributes 
      var attribute = propInfo.GetCustomAttributes(typeof(FromJsonAttribute), false).FirstOrDefault(); 
      if (attribute != null) 
       return new JsonModelBinder(context.Metadata.ModelType, attribute as IJsonAttribute); 
     } 
     return null; 
    } 
} 

JsonModelBinder.cs:

public class JsonModelBinder : IModelBinder 
{ 
    private IJsonAttribute _attribute; 
    private Type _targetType; 

    public JsonModelBinder(Type type, IJsonAttribute attribute) 
    { 
     if (type == null) throw new ArgumentNullException(nameof(type)); 
     _attribute = attribute as IJsonAttribute; 
     _targetType = type; 
    } 

    public Task BindModelAsync(ModelBindingContext bindingContext) 
    { 
     if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); 
     // Check the value sent in 
     var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
     if (valueProviderResult != ValueProviderResult.None) 
     { 
      bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); 
      // Attempt to convert the input value 
      var valueAsString = valueProviderResult.FirstValue; 
      bool success; 
      var result = _attribute.TryConvert(valueAsString, _targetType, out success); 
      if (success) 
      { 
       bindingContext.Result = ModelBindingResult.Success(result); 
       return Task.CompletedTask; 
      } 
     } 
     return Task.CompletedTask; 
    } 
} 

Cách sử dụng:

public class MyModelWrapper 
{ 
    public IList<IFormFile> Files { get; set; } 
    [FromJson] 
    public MyModel Model { get; set; } // <-- JSON will be deserialized to this object 
} 

// Controller action: 
public async Task<IActionResult> Upload(MyModelWrapper modelWrapper) 
{ 
} 

// Add custom binder provider in Startup.cs ConfigureServices 
services.AddMvc(properties => 
{ 
    properties.ModelBinderProviders.Insert(0, new JsonModelBinderProvider()); 
}); 
+0

Tôi nên sử dụng InputFormatter nào để nhận dữ liệu dưới dạng multipart/form-data? nhận lỗi 500 nếu loại nội dung là nhiều phần/biểu mẫu dữ liệu. –

0

Tôi không chắc liệu bạn có thể thực hiện hai việc trong một bước hay không.

Cách tôi đã đạt được điều này trong quá khứ là bằng cách tải tệp lên qua ajax và trả lại url của tệp trong phản hồi rồi chuyển nó cùng với yêu cầu đăng để lưu bản ghi thực.

+0

Vâng, điều đó chắc chắn có thể nhưng tôi đang cố gắng tránh hai kết nối khác nhau với máy chủ cho một nhiệm vụ chỉ để giữ mọi thứ đồng bộ giữa máy khách và máy chủ. Tôi nghĩ rằng tôi đã tìm thấy một giải pháp cho vấn đề của tôi mặc dù. Tôi sẽ đăng nó ở đây khi tôi có thêm thời gian. – Andrius

2

Tôi đã thực hiện một cách tiếp cận đơn giản hơn với những gì đã có Andrius:

JsonModelBinder.cs:

using Microsoft.AspNetCore.Mvc.ModelBinding; 

public class JsonModelBinder : IModelBinder { 
    public Task BindModelAsync(ModelBindingContext bindingContext) { 
     if (bindingContext == null) { 
      throw new ArgumentNullException(nameof(bindingContext)); 
     } 

     // Check the value sent in 
     var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
     if (valueProviderResult != ValueProviderResult.None) { 
      bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); 

      // Attempt to convert the input value 
      var valueAsString = valueProviderResult.FirstValue; 
      var result = Newtonsoft.Json.JsonConvert.DeserializeObject(valueAsString, bindingContext.ModelType); 
      if (result != null) { 
       bindingContext.Result = ModelBindingResult.Success(result); 
       return Task.CompletedTask; 
      } 
     } 
     return Task.CompletedTask; 
    } 
} 

Bây giờ chúng ta có thể sử dụng mô hình chất kết dính trực tiếp mà không cần phải trải qua một mô hình wrapper:

public async Task<IActionResult> StorePackage([ModelBinder(BinderType = typeof(JsonModelBinder))] SomeObject value, IList<IFormFile> files) { 

} 
Các vấn đề liên quan