Причина, по которой он не работает
Ваш первый метод действия -
публичное обновление асинхронной задачи ([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)
{
...
}
Рабочая демонстрация: