如何以 POST 方法在 REST API 中发送日期
Posted
技术标签:
【中文标题】如何以 POST 方法在 REST API 中发送日期【英文标题】:How to send Date in REST API in POST method 【发布时间】:2018-01-21 23:06:21 【问题描述】:我正在尝试构建带有 Spring 支持的 RESTful Web 服务。尝试发送 POST 请求时出现以下异常。
输入:
POST http://localhost:8080/InventoryDemo/item
在 JSON 负载中:
"materialId":"ID02","materialName":"Material_2","materialCategory":"LIQUID","currency":"RUPEES","unitCostInCurrency":2200.0,"quantityLevel":1000,"quantityAtDate":"2016-04-11","warehouseName":"WareHouse_2"
例外:
WARNING: Handler execution resulted in exception: Could not read document: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2016-04-11'); no single-String constructor/factory method
at [Source: java.io.PushbackInputStream@378ace07; line: 1, column: 146] (through reference chain: com.psl.inventory.model.InventorySystemModel["quantityAtDate"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2016-04-11'); no single-String constructor/factory method
at [Source: java.io.PushbackInputStream@378ace07; line: 1, column: 146] (through reference chain: com.psl.inventory.model.InventorySystemModel["quantityAtDate"])
这是我来自 @RestController 的 POST 方法:
@RequestMapping(value = "/item", method = RequestMethod.POST)
public ResponseEntity<Void> createInventorySystemModel(@RequestBody InventorySystemModel inventorySystemModel, UriComponentsBuilder ucBuilder)
System.out.println("Creating InventorySystemModel " + inventorySystemModel.getMaterialName());
if (inventorySystemService.isInventorySystemModelExist(inventorySystemModel))
System.out.println("A InventorySystemModel with name " + inventorySystemModel.getMaterialName() + " already exist");
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
inventorySystemService.saveInventoryItem(inventorySystemModel);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/user/materialId").buildAndExpand(inventorySystemModel.getMaterialId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
这是我的 POJO 类:
public class InventorySystemModel
private String materialId;
private String materialName;
private String materialCategory;
private String currency;
private double unitCostInCurrency;
private int quantityLevel;
private LocalDate quantityAtDate;
private String warehouseName;
public InventorySystemModel()
public InventorySystemModel(String materialId, String materialName,
String materialCategory, String currency,
double unitCostInCurrency, int quantityLevel, LocalDate quantityAtDate,
String warehouseName)
super();
this.materialId = materialId;
this.materialName = materialName;
this.materialCategory = materialCategory;
this.currency = currency;
this.unitCostInCurrency = unitCostInCurrency;
this.quantityLevel = quantityLevel;
this.quantityAtDate = quantityAtDate;
this.warehouseName = warehouseName;
public String getMaterialId()
return materialId;
public void setMaterialId(String materialId)
this.materialId = materialId;
public String getMaterialName()
return materialName;
public void setMaterialName(String materialName)
this.materialName = materialName;
public String getMaterialCategory()
return materialCategory;
public void setMaterialCategory(String materialCategory)
this.materialCategory = materialCategory;
public String getCurrency()
return currency;
public void setCurrency(String currency)
this.currency = currency;
public double getUnitCostInCurrency()
return unitCostInCurrency;
public void setUnitCostInCurrency(double unitCostInCurrency)
this.unitCostInCurrency = unitCostInCurrency;
public int getQuantityLevel()
return quantityLevel;
public void setQuantityLevel(int quantityLevel)
this.quantityLevel = quantityLevel;
public LocalDate getQuantityAtDate()
return quantityAtDate;
public void setQuantityAtDate(LocalDate quantityAtDate)
this.quantityAtDate = quantityAtDate;
public String getWarehouseName()
return warehouseName;
public void setWarehouseName(String warehouseName)
this.warehouseName = warehouseName;
@Override
public int hashCode()
final int prime = 31;
int result = 1;
result = prime * result
+ ((currency == null) ? 0 : currency.hashCode());
result = prime
* result
+ ((materialCategory == null) ? 0 : materialCategory.hashCode());
result = prime * result
+ ((materialId == null) ? 0 : materialId.hashCode());
result = prime * result
+ ((materialName == null) ? 0 : materialName.hashCode());
result = prime * result
+ ((quantityAtDate == null) ? 0 : quantityAtDate.hashCode());
result = prime * result + quantityLevel;
long temp;
temp = Double.doubleToLongBits(unitCostInCurrency);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result
+ ((warehouseName == null) ? 0 : warehouseName.hashCode());
return result;
@Override
public boolean equals(Object obj)
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InventorySystemModel other = (InventorySystemModel) obj;
if (currency == null)
if (other.currency != null)
return false;
else if (!currency.equals(other.currency))
return false;
if (materialCategory == null)
if (other.materialCategory != null)
return false;
else if (!materialCategory.equals(other.materialCategory))
return false;
if (materialId == null)
if (other.materialId != null)
return false;
else if (!materialId.equals(other.materialId))
return false;
if (materialName == null)
if (other.materialName != null)
return false;
else if (!materialName.equals(other.materialName))
return false;
if (quantityAtDate == null)
if (other.quantityAtDate != null)
return false;
else if (!quantityAtDate.equals(other.quantityAtDate))
return false;
if (quantityLevel != other.quantityLevel)
return false;
if (Double.doubleToLongBits(unitCostInCurrency) != Double
.doubleToLongBits(other.unitCostInCurrency))
return false;
if (warehouseName == null)
if (other.warehouseName != null)
return false;
else if (!warehouseName.equals(other.warehouseName))
return false;
return true;
@Override
public String toString()
return "InventorySystemModel [materialId=" + materialId
+ ", materialName=" + materialName + ", materialCategory="
+ materialCategory + ", currency=" + currency
+ ", unitCostInCurrency=" + unitCostInCurrency
+ ", quantityLevel=" + quantityLevel + ", quantityAtDate="
+ quantityAtDate + ", warehouseName=" + warehouseName + "]";
仅供参考:我确实检查了this 的帖子,但没有得到关于我需要在哪里进行修改的线索。
我正在使用 Java 8 和 Spring 4.2
有人可以详细解释一下我到底需要在这里做什么。 我还想要相同的日期格式,当我点击 GET 请求时。
谢谢。
【问题讨论】:
拥有 public void setQuantityAtDate(String quantityAtDate) 并使用 dateFormatter 获取字符串输入并将其转换为 Date 不是更容易吗? 这个问题和这个***.com/questions/29571587/…比较相似 【参考方案1】:使用反序列化器来解析 LocalDate。
添加 Maven 依赖 -
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.8.10</version>
</dependency>
如果你的 RESTful 服务是直接解析 bean,添加下面
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
@PostMapping(value = "/xyz")
@JsonDeserialize(using = LocalDateDeserializer.class)
public ResponseEntity <String> testMethod (@RequestBody Bean bean)
否则,在 bean 类中添加 deserializer
。
【讨论】:
【参考方案2】:错误
JsonMappingException
是 Java 的 JSON 解析器 Jackson 抛出的异常。它表示将 JSON 映射到 Java bean 时出现致命问题。
在这种情况下,字符串 2016-04-11
似乎无法从 Java 8 解析为 LocalDate
。
如何解决
Jackson 支持 Java 8 日期类型,但需要以下依赖:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>$jackson.version</version>
</dependency>
然后配置你的ObjectMapper
:
@Configuration
public class JacksonConfig
@Bean
public ObjectMapper createObjectMapper()
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
默认情况下,日期将以ISO 8601 格式序列化。如果要更改格式,可以使用@JsonFormat
:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
private LocalDate date;
不需要自定义(反)序列化程序。
【讨论】:
【参考方案3】:您可以自定义 LocalDate 反序列化器。当 LocalDate 变量的 setter 方法被调用时,这个 Deserializer 将被调用。
步骤如下:
定义一个自定义反序列化器
public class LocalDateDeserializer extends JsonDeserializer<LocalDate>
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("required format");
LocalDate localDate = null;
localDate = LocalDate.parse(p.getText(), formatter);
return localDate;
注意:LocalDate.parse 方法的参考。
在变量上方定义@JsonDeserialize注解
@JsonDeserialize(using=LocalDateDeserializer.class)
private LocalDate quantityAtDate;
对于使用@JsonDeserialize 注解导入如下:
导入 com.fasterxml.jackson.databind.annotation.JsonDeserialize;
希望这会有所帮助。
【讨论】:
我认为我们必须像这里提到的 baeldung.com/jackson-deserialization 那样使用 objectmapper 注册这个反序列化器。 @Barath 实际上在我的 Spring MVC 应用程序中我没有注册它。可能是 Spring 在找到注释时自动执行此操作。【参考方案4】:利用@JsonFormat定义日期格式
http://www.baeldung.com/jackson-serialize-dates
public class InventorySystemModel
private String materialId;
private String materialName;
private String materialCategory;
private String currency;
private double unitCostInCurrency;
private int quantityLevel;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date quantityAtDate;
private String warehouseName;
//getters and setters
请求:
"materialId": "ID02",
"materialName": "Material_2",
"materialCategory": "LIQUID",
"currency": "RUPEES",
"unitCostInCurrency": 2200.0,
"quantityLevel": 1000,
"quantityAtDate": "2016-04-11",
"warehouseName": "WareHouse_2"
回应:
InventorySystemModel [materialId=ID02, materialName=Material_2, materialCategory=LIQUID, currency=RUPEES, unitCostInCurrency=2200.0, quantityLevel=1000, quantityAtDate=Mon Apr 11 05:30:00 IST 2016, warehouseName=WareHouse_2]
【讨论】:
以上是关于如何以 POST 方法在 REST API 中发送日期的主要内容,如果未能解决你的问题,请参考以下文章
如何在swift 3中制作一个json对象以通过POST api发送它?
如何仅在 Django Rest Framework 中的另一个 API 方法中调用 API POST 方法