FromForm是否不反序列化FormData对象以在路由的参数中使用?

时间:2019-06-09 19:52:10

标签: asp.net-core

我的asp.net核心后端服务器中有一条更新路由

[HttpPatch("{id}")]
public async Task<IActionResult> Update([FromForm] GearItemViewModel gearItem)
{
    ...
}

从我的角度应用程序中,我正在发送包含FormData的请求。 FormData有两个对象,一个称为“ gearImages”,另一个称为“ gearItem”。

this.selectedFileFormData.append(
  "gearItem",
  JSON.stringify({ id: 1, name: "tst", price: 30, inStock: false })
);



for (let index = 0; index < 3; index++) {
  this.selectedFileFormData.append("gearImages", filesObj[index]);
}

当我发出以下请求时:

return this.http
  .patch<GearItem>(
    `${this.merchandiseUrl}/${gearItem.id}`,
    gearItem.formData
  )

请求到达正确的路由,但是gearItem的参数未填充有效数据。它具有所有null值。

当我修改接受路线时:

[HttpPatch("{id}")]
public async Task<IActionResult> Update([FromForm] List<IFormFile> gearImages)
{
    ...
}

该参数已成功填充。

我可以从请求对象手动反序列化gearItem对象:

JsonConvert.DeserializeObject<GearItemViewModel>(Request.Form["gearItem"]);

任何想法为什么这可能行不通?

2 个答案:

答案 0 :(得分:1)

它不起作用的原因

您的第一个动作方法是

  

公共异步任务更新([FromForm] GearItemViewModel gearItem)

通常,它将接受application/x-www-form-urlencoded multipart/form-data 。但是,在使用application/x-www-form-urlencoded时,您不能同时发送图像文件(除非您对图像文件进行编码,例如base64,但这并不好)。由于相同的原因,您无法在application/json 中发送有效载荷。

这意味着您的操作方法需要 multipart/form-data 中的数据,如下所示:

POST /.../Update HTTP/1.1
Content-Type: multipart/form-data; boundary=----My.Boundary

------My.Boundary
Content-Disposition: form-data; name="id"

1    
------My.Boundary
Content-Disposition: form-data; name="name"

tst    
------My.Boundary
Content-Disposition: form-data; name="price"

30
------My.Boundary
Content-Disposition: form-data; name="inStock"

false
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="1.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image1}
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="2.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image2}
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="3.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image3}
------My.Boundary--

但是,您发送到服务器的是:

POST /.../Update HTTP/1.1
Content-Type: multipart/form-data; boundary=----My.Boundary

------My.Boundary
Content-Disposition: form-data; name="gearItem"

{ "id": 1, "name": "tst", "price": 30, "inStock": false }
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="1.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image1}
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="2.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image2}
------My.Boundary
Content-Disposition: form-data; name="gearImages"; filename="3.jpg"
Content-Type: application/octet-stream

{bytes-of-your-image3}
------My.Boundary--

结果,服务器端没有gearItem


如何解决

假设您的GearItemViewModel是:

public class GearItemViewModel
{
    public long Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool InStock { get; set; }

    public IList<IFormFile> GearImages {get;set;}
}

您需要按以下方式构造FormData(我只需稍加更改即可复制并粘贴您的代码)

this.selectedFileFormData.append("id","1");
this.selectedFileFormData.append("name","tst");
this.selectedFileFormData.append("price","30");
this.selectedFileFormData.append("inStock","false");
for (let index = 0; index < 3; index++) {
  this.selectedFileFormData.append("gearImages", this.filesObj[index]);
}

或者您可以使用表单元素初始化FormData

var formElement = document.getElementById("yourFormId");
var selectedFileFormData= new FormData(formElement);

然后发送FormData,您将自动获得有效载荷:

this.http.patch<GearItem>(`${this.merchandiseUrl}/${gearItem.id}`, this.selectedFileFormData)
  .subscribe(res => {
      ...
  })

[编辑]

  

您是否知道是否可以使用formData并附加一个表示“尺寸”的对象

假设SizeGearSize的形状是:

// typescript 
enum Size { NONE = 0, XS = 1, S = 2, XXL = 6 }

interface GearSize 
{
  id : Number,
  available: boolean,
  color : string,
  size: Size,
}

假设您有4个gearSizes:

var gearSizes: GearSize[] = [
  {id:1,available:true, color:"red",size:Size.S},
  {id:2,available:false, color:"blue",size:Size.XS},
  {id:3,available:true, color:"green",size:Size.XXL},
  {id:4,available:true, color:"yellow",size:Size.NONE},
];  

要发送这些gearSize,只需创建格式为sizes[index].property的字段即可:

// a helper function that appends its fields to formdata
appendOneGearSize(formData: FormData, index: Number, size:GearSize){
    formData.append(`sizes[${index}].id`,size.id.toString());
    formData.append(`sizes[${index}].available`,size.avaiable.toString());
    formData.append(`sizes[${index}].color`,size.color.toString());
    formData.append(`sizes[${index}].size`,size.size.valueOf().toString());
}

for(let index=0; index < gearSizes.length; index++){
  this.appendOneGearSize(this.selectedFileFormData,index,gearSizes[index]);
}

最后,这是服务器端操作方法:

public async Task<IActionResult> Update([FromForm] GearItemViewModel gearItem)
{
    ...
}

有效的演示: enter image description here

答案 1 :(得分:0)

根据我对asp.net核心的经验,FromForm接受application / x-www-form-urlencoded内容.....如果要使用json,则可以改用FromBody。