FromForm не десериализовать объект FormData для использования в параметрах маршрута? - PullRequest
0 голосов
/ 09 июня 2019

У меня есть маршрут обновления на моем базовом внутреннем сервере 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 ]

1 голос
/ 10 июня 2019

Причина, по которой он не работает

Ваш первый метод действия -

публичное обновление асинхронной задачи ([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

<b>------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</b>
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"

<b>{ "id": 1, "name": "tst", "price": 30, "inStock": false }</b>
------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 и добавить, например, объект, который представляет "Размеры"

Предположим, что форма Size & GearSize имеет вид:

// 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},
];  

Для отправки этих передач,просто создайте поля, которые имеют формуиз 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

0 голосов
/ 10 июня 2019

Исходя из моего опыта работы с ядром asp.net, FromForm принимает контент application / x-www-form-urlencoded ..... Если вы хотите использовать json, вы можете использовать FromBody.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...