Spring POST 400 Bad Request Postman при изменении класса с 2 на 1 параметр - PullRequest
1 голос
/ 27 апреля 2020

У меня есть следующий код в моем классе @RestController:

@RequestMapping("api/")
@RestController
public class RecommendationsController {
@PostMapping(path = "cart")
    public List<RecommendationDTO> getCartRecommendations(@NonNull @RequestBody List<CartItemDTO> cart){
        System.out.println(cart);
        return null;
    }
}

Это код в моем классе CartItemDTO:

public class CartItemDTO {

    private String productId;
    private Double quantity;


    public CartItemDTO(String productId, Double quantity) {
        this.productId = productId;
        this.quantity = quantity;
    }

    public String getProductId() {
        return productId;
    }

    public Double getQuantity(){
        return quantity;
}

И это запрос, который я отправляю с почтальоном :

[
    {
        "productId": "20000010",
        "quantity": 5.0;
    },
    {
        "productId": "20000011",
        "quantity": 7.0;
    }
]

Это работает, но когда я изменяю свой код следующим образом, я получаю неверный запрос: плохой синтаксис

public class CartItemDTO {

    private String productId;


    public CartItemDTO(String productId) {
        this.productId = productId;
    }

    public String getProductId() {
        return productId;
    }
}

с запросом:

[
    {
        "productId": "20000010"
    },
    {
        "productId": "20000011"
    }
]

У кого-нибудь есть идеи, что может быть не так?

Ответы [ 2 ]

1 голос
/ 27 апреля 2020

Основная проблема заключается в том, что Джексон не может создать экземпляр DTO.
Два способа решения этой проблемы:
1. Укажите конструктор по умолчанию:
- когда вы задаете параметризованный конструктор, тогда Java компилятор не добавит конструктор по умолчанию .

Теперь ваш первый запрос:

curl --location --request POST 'localhost:8080/cart' \
--header 'Content-Type: application/json' \
--data-raw '[
    {
        "productId": "20000010",
        "quantity": 5.0
    },
    {
        "productId": "20000011",
        "quantity": 7.0
    }
]'

Выдает ошибку:

    "timestamp": "2020-04-27T12:08:28.497+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Type definition error: [simple type, class hello.dto.CartItemDTO]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `hello.dto.CartItemDTO` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (PushbackInputStream); line: 3, column: 9] (through reference chain: java.util.ArrayList[0])",
    "path": "/cart"

при добавлении конструктора по умолчанию до DTO все работает нормально, как и ожидалось.

public class CartItemDTO {

    private String productId;
    private Double quantity;

    public CartItemDTO() {
    }

    public CartItemDTO(String productId, Double quantity) {
        this.productId = productId;
        this.quantity = quantity;
    }

    public String getProductId() {
        return productId;
    }

    public Double getQuantity() {
        return quantity;
    }
}

Поскольку у OP нет объекта RecommendationDTO, в качестве вывода добавляется только System.out.println:

[hello.dto.CartItemDTO@145e35d6, hello.dto.CartItemDTO@25df553f]
Только ProductId в DTO
public class CartItemDTO {
    private String productId;

    public CartItemDTO() {
    }

    public CartItemDTO(String productId, Double quantity) {
        this.productId = productId;
    }

    public String getProductId() {
        return productId;
    }
}

Запрос:

curl --location --request POST 'localhost:8080/cart' \
--header 'Content-Type: application/json' \
--data-raw '[
    {
        "productId": "20000010"
    },
    {
        "productId": "20000011"
    }
]'

Вывод:

[hello.dto.CartItemDTO@42ad1f23, hello.dto.CartItemDTO@777d8eb3]
Решение второе: необходимо указать Джексону создать объект dto, используя конструктор с полями экземпляра, как показано ниже:

изменить DTO на

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class CartItemDTO {

    private String productId;
    private Double quantity;

    @JsonCreator
    public CartItemDTO(@JsonProperty(value = "productId", required = true) String productId,
                       @JsonProperty(value = "quantity", required = true) Double quantity) {
        this.productId = productId;
        this.quantity = quantity;
    }

    public String getProductId() {
        return productId;
    }

    public Double getQuantity() {
        return quantity;
    }
}

ИЛИ только с productId

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class CartItemDTO {

    private String productId;

    @JsonCreator
    public CartItemDTO(@JsonProperty(value = "productId", required = true) String productId) {
        this.productId = productId;
    }

    public String getProductId() {
        return productId;
    }
}
1 голос
/ 27 апреля 2020

Кажется, есть проблема с более быстрым xml Джексоном, когда он пытается автоматически угадать JsonCreator, когда конструктор имеет только 1 аргумент.

Лучшее решение, которое я нашел, это добавить @JsonCreator аннотация для конструктора, поэтому Джексону не придется угадывать, какого создателя ему следует использовать.

Примерно так:

import com.fasterxml.jackson.annotation.JsonCreator;

public class CartItemDTO {

    private String productId;

    @JsonCreator
    public CartItemDTO( String productId) {
        this.productId = productId;
    }

    public String getProductId() {
        return productId;
    }

}

Обновление:

Видимо, это известное ограничение для более быстрого xml Джексона: https://github.com/FasterXML/jackson-modules-java8/issues/8

...