Как отправить массив объектов в методе post (Angular) и принять их (Spring)?

Имеется массив объектов на клиенте (Angular)

basket: IProducts[] = [];

Отправляю его через

this.basketService.getOrder(this.basket).subscribe();

Метод getOrder():

getOrder(basket: IProducts[]): Observable<any>{
return this.http.post(
  BASKET_API +'getOrder',
  {
    basket

  },
  httpOptions
)

} Пытаюсь получить его ( Spring Boot)

@PostMapping("/getOrder")
public void doOrder( @RequestBody List<Product> basket)

и получаю следующую ошибку:

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<com.shopping.crud.model.Product>` from Object value (token `JsonToken.START_OBJECT`)]

в чем проблема?


Ответы (1 шт):

Автор решения: Михаил Ребров

Задавая вопросы, пожалуйста, старайтесь приводить минимально воспроизводимый пример.

Из Вашего вопроса непонятно как и что Вы отсылаете, поэтому ниже будет аналогичный пример отправки предполагаемой корзины со списком предполагаемых продуктов на предполагаемый контроллер, принимающий предполагаемую модель данных.

Для примера я создал модель:

package com.stackoverflow.ru.model;

public class Product {

    private long id;
    private String name;
    private String sku;
    private Float price;
    private Integer quantity;
    
    // чтобы не было проблем с переносимостью - указал геттеры и сеттеры в кратком виде
    // используете вы ломбок или нет - не знаю
    // что с ними делать дальше - разберетесь сами
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getSku() { return sku; }
    public void setSku(String sku) { this.sku = sku; }
    public Float getPrice() { return price; }
    public void setPrice(Float price) { this.price = price; }
    public Integer getQuantity() { return quantity; }
    public void setQuantity(Integer quantity) { this.quantity = quantity; }
}

Создал пример контроллера:

package com.stackoverflow.ru.controller;

import java.util.List;
import com.stackoverflow.ru.model.Product;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/basket")
public class BasketController {
    @PostMapping("/getOrder")
    public ResponseEntity<String> doOrder( @RequestBody List<Product> basket){
        System.out.println(basket.toString());
        return ResponseEntity.ok().build();
    }
}

на фронте определил корзину со списком продуктов:

 let basket = [
     {
         id: 1,
         name: 'Чайный гриб',
         sku: 'ART-0987-3456-5234-7345',
         price: 100.2,
         quantity: 1
     },
     {
         id: 2,
         name: 'Шкура волчья',
         sku: 'ART-7254-8364-6234-7235',
         price: 99.99,
         quantity: 1
     },
     {
         id: 3,
         name: 'ART-84426-3252-2353-7647',
         sku: 'Вино пшеничное',
         price: 450.89,
         quantity: 2
     }
 ];

и отослал в эндпоинт следующим образом:

fetch('/basket/getOrder',{
    method: 'POST',
    headers: {
            'Content-Type': 'application/json'
        },
    body: JSON.stringify(basket),
})

Важно:

  • в headers указываем тип контента
    'Content-Type': 'application/json'
  • в тело запроса кладём сериализованный массив объектов
    JSON.stringify(basket)

Запрос в консоли: введите сюда описание изображения

В контроллере в отладчике получаем: введите сюда описание изображения

Все работает


UPD

Вопрос: можно как либо передать второй аргумент в body?

Все очень просто: Вам просто нужно описать модель того запроса, который Вы ожидаете получить.

Если Вы в этой корзине помимо списка продуктов хотите передавать username, то создайте класс, который будет в себе содержать и список продуктов и username

package com.stackoverflow.ru.model;

import java.util.List;

public class Basket {
    private String username;
    private List<Product> items;

    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public List<Product> getItems() { return items; }
    public void setItems(List<Product> items) { this.items = items; }
}

и принимайте его в эндпоинте в качестве тела запроса

public ResponseEntity<String> doOrder(@RequestBody Basket basket)

контроллер полностью

package com.stackoverflow.ru.controller;

import java.util.List;

import com.stackoverflow.ru.model.Basket;
import com.stackoverflow.ru.model.Product;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/basket")
public class BasketController {
    @PostMapping("/getOrder")
    public ResponseEntity<String> doOrder( @RequestBody Basket basket) {
        System.out.println(basket.toString());
        return ResponseEntity.ok().build();
    }
}

Собираете на фронте нужный объект с логином и списком продуктов

 let basket = {
    username: 'rebrov_mihail',
    items: [
     {
         id: 1,
         name: 'Чайный гриб',
         sku: 'ART-0987-3456-5234-7345',
         price: 100.2,
         quantity: 1
     },
     {
         id: 2,
         name: 'Шкура волчья',
         sku: 'ART-7254-8364-6234-7235',
         price: 99.99,
         quantity: 1
     },
     {
         id: 3,
         name: 'ART-84426-3252-2353-7647',
         sku: 'Вино пшеничное',
         price: 450.89,
         quantity: 2
     }
 ]
 };

и отсылаете таким же образом

fetch('/basket/getOrder',{
    method: 'POST',
    headers: {
            'Content-Type': 'application/json'
        },
    body: JSON.stringify(basket),
})

Итог: введите сюда описание изображения


Касательно ангуляра... насколько я понимаю, хедеры там указываются так

const myHeaders = new HttpHeaders({'Content-Type': 'application/json'});
this.http.post<any>('/basket/getOrder', basket, { headers: myHeaders }).subscribe(data => {
    // ...some code
});

но начиная с версии 5.0.0-beta.6 создание HttpHeaders можно опустить

this.http.post<any>('/basket/getOrder', basket, { headers: {'Content-Type': 'application/json'} }).subscribe(data => {
    // ...some code
});
→ Ссылка