ASP. NET Базовая публикация данных формы IFormFile с помощью ViewModel с использованием HTTPClient - PullRequest
1 голос
/ 08 апреля 2020

Я создаю WebAPI и WebApp, оба они используют ASP. NET Core 2.1

Мое веб-приложение пытается опубликовать (в веб-API) ViewModel, который содержит IFormFile и другие свойства. Я знаю, что должен использовать MultipartFormDataContent для публикации IFormFile, но я не знаю, как реализовать его с моей ViewModel, потому что моя ViewModel имеет List другой модели.

Я уже пытаюсь найти некоторые решения в Google, но я нашел только решения с простой ViewModel без списка, подобные этим:

{ ссылка }

{ ссылка }.


Есть ли какое-либо решение, например

var multiContent = new MultipartFormDataContent();
var viewModelHttpContent= new StreamContent(viewModel);
MultiContent.Add(viewModelHttpContent, "viewModel");
var response = await client.PostAsJsonAsync("/some/url", multiContent);

, поэтому мне не нужно добавлять свою собственность к MultipartFormDataContent по одному и публиковать это как json.


Вот мой Web App ViewModel

public class CreateDataViewModel
{
    public string PrimaryKeyNumber{ get; set; }

    public List<Currency> ListOfCurrency { get; set; }

    public IList<DataDetail> dataDetails { get; set; }

    [DataType(DataType.Upload)]
    public IFormFile Attachment { get; set; }

    //And other properties like Boolean, Datetime?, string
}

Вот мой контроллер Web App

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(CreateDataViewModel viewModel)
    {
        //How to implement MultipartFormDataContent with my ViewModel in here ?

        //My code below returns Could not create an instance of type Microsoft.AspNetCore.Http.IHeaderDictionary. Type is an interface or abstract class and cannot be instantiated. Path 'Attachment.Headers.Content-Disposition', line 1, position 723.
        //It works fine if I don't upload a file
        HttpResponseMessage res = await _client.PostAsJsonAsync<CreateDataViewModel>("api/data/create", viewModel);

        var result = res.Content.ReadAsStringAsync().Result;

        if (res.IsSuccessStatusCode)
        {
            TempData["FlashMessageSuccess"] = "Data have been submitted";
            return RedirectToAction("Index", "Home"); ;
        }


        //Code for error checking

    }

Вот мой контроллер Web API, который ловит почтовый ответ, используя CreateDataViewModel в качестве параметра.

[HttpPost]
[Route("[action]")]
public async Task<IActionResult> Create(CreateDataViewModel viewModel)
{
    //Code to validate then save the data
}

1 Ответ

1 голос
/ 09 апреля 2020

не знаю, как реализовать это с моей ViewModel, потому что у моей ViewModel есть Список другой модели

Вы можете обратиться к следующему фрагменту кода и реализовать связыватель пользовательской модели для достижения ваших требование.

var multipartContent = new MultipartFormDataContent();

multipartContent.Add(new StringContent(viewModel.PrimaryKeyNumber), "PrimaryKeyNumber");


multipartContent.Add(new StringContent(JsonConvert.SerializeObject(viewModel.ListOfCurrency)), "ListOfCurrency");
multipartContent.Add(new StringContent(JsonConvert.SerializeObject(viewModel.dataDetails)), "dataDetails");


multipartContent.Add(new StreamContent(viewModel.Attachment.OpenReadStream()), "Attachment", viewModel.Attachment.FileName);


var response = await client.PostAsync("url_here", multipartContent);

Реализация пользовательского связывателя модели для преобразования данных входящего запроса

public Task BindModelAsync(ModelBindingContext bindingContext)
{
    if (bindingContext == null)
    {
        throw new ArgumentNullException(nameof(bindingContext));
    }
    // code logic here
    // ...


    // ...
    // fetch the value of the argument by name
    // and populate corresponding properties of your view model

    var model = new CreateDataViewModel()
    {
        PrimaryKeyNumber = bindingContext.ValueProvider.GetValue("PrimaryKeyNumber").FirstOrDefault(),
        ListOfCurrency = JsonConvert.DeserializeObject<List<Currency>>(bindingContext.ValueProvider.GetValue("ListOfCurrency").FirstOrDefault()),
        dataDetails = JsonConvert.DeserializeObject<List<DataDetail>>(bindingContext.ValueProvider.GetValue("dataDetails").FirstOrDefault()),
        Attachment = bindingContext.ActionContext.HttpContext.Request.Form.Files.FirstOrDefault()
    };

    bindingContext.Result = ModelBindingResult.Success(model);
    return Task.CompletedTask;
}

Применение его к методу действия API

public async Task<IActionResult> Create([ModelBinder(BinderType = typeof(CustomModelBinder))]CreateDataViewModel viewModel)

Результат теста

enter image description here

...