【问题标题】:ASP.NET Core Post form data IFormFile with ViewModel using HTTPClientASP.NET Core 使用 HTTPClient 使用 ViewModel 发布表单数据 IFormFile
【发布时间】:2020-07-20 17:25:47
【问题描述】:

我正在构建 WebAPI 和 WebApp,它们都使用 ASP.NET Core 2.1

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

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

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

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


有没有类似的解决方案

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

所以我不必将我的属性一一添加到MultipartFormDataContent 并将其发布为json。


这是我的网络应用视图模型

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
}

这是我的网络应用控制器

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

【问题讨论】:

    标签: asp.net-mvc rest api asp.net-core asp.net-web-api


    【解决方案1】:

    不知道如何用我的 ViewModel 实现它,因为我的 ViewModel 有其他模型的列表

    您可以参考以下代码 sn-p 并实现自定义模型绑定器以实现您的要求。

    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)
    

    测试结果

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-26
      相关资源
      最近更新 更多