本文介绍了ASP.NET Core Post 表单数据 IFormFile 和 ViewModel 使用 HTTPClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建 WebAPI &WebApp,两者都使用 ASP.NET Core 2.1

我的 Web 应用程序正在尝试使用包含 IFormFile 和其他属性的 ViewModel 向 Web API 发送发布请求.我知道我必须使用 MultipartFormDataContent 来发布 IFormFile,但我不知道如何用我的 ViewModel 实现它,因为我的 ViewModel 有其他模型的 List.

我已经尝试在谷歌上搜索一些解决方案,但我只找到了没有 List 的简单 ViewModel 解决方案,如下所示:

I'm building WebAPI & WebApp, both of them using ASP.NET Core 2.1

My Web App is trying to send post request to the Web API using ViewModel that contains IFormFile and other properties. I know I have to use MultipartFormDataContent to post IFormFile, but I don't know how to implement it with my ViewModel because my ViewModel has List of other model.

I already try to google some solutions, but I only found solutions with simple ViewModel without List like these :

https://stackoverflow.com/a/41511354/7906006

https://stackoverflow.com/a/55424886/7906006.


Is there any solution like

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

so i don't have to add my property to MultipartFormDataContent one by one and post it as json.


Here's my 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
}

Here's my Web App controller

    [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

    }

Here's my Web API controller that catches the post response using CreateDataViewModel as parameter.

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

You can refer to following code snippet and implement a custom model binder to achieve your requirement.

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

Implement a custom model binder to convert incoming request data

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

Apply it on API action method

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

Test Result

这篇关于ASP.NET Core Post 表单数据 IFormFile 和 ViewModel 使用 HTTPClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-26 05:34