假設我有一個這樣的 ViewModel 來自 ASP.NET MVC 中的表單提交:
public class EditProfileViewModel
{
public string DisplayName { get; set; }
public IFormFile Photo { get; set; }
}
我可以很容易地在控制器中接收到這個:
public async Task<IActionResult> OnPost([FromForm] EditProfileViewModel edits)
{
// edits contains form data, great
}
但現在我想將此轉發到后端 API,也使用 ASP.NET。包括檔案上傳。最簡單的方法似乎是使用 MultipartFormDataContent 創建一個帶有 HttpClient 的新表單 POST。但據我所知,雖然從表單內容到模型類的轉換在接收請求時是透明的,但創建MultipartFormDataContent 需要對鍵值對進行硬編碼。
我知道可以做這樣的事情:
var form = new MultipartFormDataContent()
form.Add(new StringContent(edits.DisplayName), "displayName")
var content = new StreamContent(edits.Photo.OpenReadStream());
content.Headers.ContentType = MediaTypeHeaderValue.Parse(file.ContentType)
form.Add(content, "photo") // Is this the right capitalization ?? See how error prone this is?
var result = await client.PostAsync("some-api", form);
但這是冗長且容易出錯的重復宣告。此外,要在接收端使用相同的模型類進行反序列化,需要了解轉換魔法。
有沒有更好的方法將模型類轉換回 MultipartFormDataContent?或者,如果這是一個 XY 問題,將這個表單模型完全轉發到后端 ASP.NET API 的更好方法是什么?
uj5u.com熱心網友回復:
需要轉換魔法的知識
這就是重點 -MultipartFormDataContent可用于將表單發布到任何Web 應用程式,并且代碼不知道底層決議魔法,因為它需要被告知接收端如何期望其格式。
一個 web 框架可以區分大小寫,而另一個需要小寫或完全匹配的大小寫。或者對于陣列,一個可以支持欄位名稱的重復 ( name="foo", name="foo", ...) 另一個可能需要[]后綴 ( name="foo[]", name="foo[]", ...) 而另一個可能需要索引 ( name="foo[0]", name="foo[1]", ...)。
更不用說多個級別的屬性了:name="foo.bar", name="foo_bar", ...
因此,您必須撰寫自己的屬性到欄位名映射代碼,知道您要發布到哪個框架。
uj5u.com熱心網友回復:
我用一些擴展方法和反射解決了“對鍵值對進行硬編碼”的問題:
public static class MultiContentExtensionMethods
{
public static void AddDto<TDto>(this MultipartFormDataContent multiContent, TDto dto)
{
List<PropertyInfo> propertyInfos = dto.GetType().GetProperties().ToList();
foreach(var propertyInfo in propertyInfos)
{
if (propertyInfo.PropertyType != typeof(IFormFile) &&
!propertyInfo.PropertyType.Name.Contains("List"))
{
if(propertyInfo.GetValue(dto) is not null)
multiContent.Add(new StringContent(propertyInfo.GetValue(dto).ToString()), propertyInfo.Name);
}
else if(propertyInfo.PropertyType != typeof(IFormFile) &&
propertyInfo.PropertyType.Name.Contains("List"))
{
var list = (IList)propertyInfo.GetValue(dto, null);
if(list is not null)
foreach (var item in list)
{
multiContent.Add(new StringContent(item.ToString()), propertyInfo.Name);
}
}
}
}
public static void AddFile(this MultipartFormDataContent multiContent, IFormFile file, string name)
{
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
{
data = br.ReadBytes((int)file.OpenReadStream().Length);
}
ByteArrayContent bytes = new(data);
multiContent.Add(bytes, name, file.FileName);
}
}
用法:
在這個例子中,ViewModel 和 Dto 之間沒有區別,所以你可以使用 ViewModel
public class EditProfileDto
{
public string DisplayName { get; set; }
public IFormFile Photo { get; set; }
}
EditProfileDto dto = new()
{
DisplayName = edits.DisplayName
};
MultipartFormDataContent form = new();
form.AddDto(dto);
form.AddFile(edits.Photo, nameof(dto.Photo));
var result = await client.PostAsync("some-api", form);
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/380095.html
標籤:C# 网站 asp.net-mvc asp.net-web-api
