java - 保存结构化 JSON 对象的错误请求
问题描述
我创建了一个简单的 Web REST 应用程序,它必须将“更改票证”保存到数据库。但是在我尝试使用 JSON 作为正文创建 POST 请求后,出现错误:
2020-11-28 14:06:10.449 DEBUG 14864 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 400 BAD_REQUEST
2020-11-28 14:06:10.453 DEBUG 14864 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for POST "/error", parameters={}
2020-11-28 14:06:10.455 DEBUG 14864 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2020-11-28 14:06:10.464 DEBUG 14864 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2020-11-28 14:06:10.464 DEBUG 14864 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Sat Nov 28 14:06:10 CET 2020, status=400, error=Bad Request, message=, path=/change/save}]
2020-11-28 14:06:10.476 DEBUG 14864 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 400
我正在使用 Spring Boot 2、MySQL 作为 DB 和 MapStruct 将 DTO 转换为实体,反之亦然。我也使用 Project lombok 来摆脱一些样板代码
我的控制器 POST 方法如下所示:
@RestController
@Slf4j
@RequestMapping({"/change"})
public class ChangeTicketController {
@PostMapping(value = "/save", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> save(@RequestBody @Validated ChangeTicketDto changeTicketDto){
ChangeTicket mappedChangeTicket = changeTicketMapper.changeTicketDtoToChangeTicket(changeTicketDto);
ChangeTicket savedTicket = changeService.save(mappedChangeTicket);
return ResponseEntity.created(URI.create(BASE_URL + "/save/" + savedTicket.getChangeId()))
.body("Change Ticket has been saved");
}
}
我正在保存的实体如下所示:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class ChangeTicket extends BaseItem{
//some constructor
@NotBlank
private String changeId; // public ID set by user
@NotNull
@Enumerated(value = EnumType.STRING)
private ChangeType changeType;
@NotBlank
@Size(min = 15, max = 500)
@Column(length = 500)
private String description;
}
超类:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public class BaseItem {
@JsonIgnore
@Id
@GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
private String id; // Secret ID generated by DB
@Enumerated(value = EnumType.STRING)
@NotNull
@Column(name = "item_status")
private ItemStatus itemStatus;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private Timestamp createdAt;
@UpdateTimestamp
@Column(name = "updated_at",updatable = true)
private Timestamp updatedAt;
@Column(name = "closed_at", updatable = false)
private Timestamp closedAt;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "incidentSolver_id", referencedColumnName = "id") // owning side
private IncidentSolver incidentSolver;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
}
这是我通过 POSTMAN 发送到的示例 JSON 代码,作为 POST 请求发送到:http://localhost:8080/change/save
{
"changeId": "86edd7ea-4c37-4dd9-a55c-aeea171e0b42",
"changeType": "OS_SETTINGS_CHANGE",
"description": "description",
"itemStatus": "OPEN",
"createdAt": "2020-11-28T12:51:58+00:00",
"updatedAt": "2020-11-28T12:55:08+00:00",
"closedAt":"",
"incidentSolver": [
{
"incidentId": "015f3e95-de08-4035-9052-9d40ad2b7af6",
"userName": "ThisDude"
}
],
"user": [
{
"userId": "3d00339a-5757-4ada-a316-6705ff603d96",
"userType": "CUSTOMER",
"userName": "WednesdayDude"
}
]
}
我不知道杰克逊不知道如何反序列化/序列化子对象和父对象是否有问题,或者我是否错误地构建了我的 Json。如果您需要查看我的服务类存储库,我将编辑这篇文章以显示它们。
编辑:添加 ChangeTicketDTO
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChangeTicketDto extends BaseItemDto {
private String changeId; // public ID set by user
private ChangeType changeType;
private String description;
}
基本项目DTO
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class BaseItemDto {
private ItemStatus itemStatus;
private Timestamp createdAt;
private Timestamp updatedAt;
private Timestamp closedAt;
private IncidentSolver incidentSolver;
private User user;
}
谢谢大家的帮助
解决方案
我回来了一个对我有用的解决方案。我试图测试并查看可能导致此问题的原因,结果证明,当您构造错误的 JSON 请求正文时会出现此问题。
正如您在我的帖子中看到的那样,我正在使用 JSON 正文向我的控制器发送 POST 请求。但是我想到了编写一个将我的对象转换为 JSON 的测试方法,所以我将确保我的 JSON 是以正确的方式构造的。在这里你可以看到它:
@ExtendWith(MockitoExtension.class)
class ChangeServiceImplTest {
ChangeTicketDto changeTicketDto;
@Test
public void testConversionOfObject() throws JsonProcessingException {
changeTicketDto = changeTicketDto.builder().changeId("StringID")
.description("SOME TEXT")
.changeType(ChangeType.INSTALLATION_OF_OS)
.incidentSolver(IncidentSolver.builder().userName("DUDE").id("ID").build())
.user(User.builder().userName("USERNAME").userType(UserType.USER).build())
.closedAt(Timestamp.from(Instant.now()))
.createdAt(Timestamp.from(Instant.now()))
.updatedAt(Timestamp.from(Instant.now()))
.itemStatus(ItemStatus.OPEN)
.build();
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(changeTicketDto);
System.out.println(json);
}
}
此方法返回一个简单的 JSON 字符串,如下所示:
{
"itemStatus": "OPEN",
"createdAt": 1607031646362,
"updatedAt": 1607031646362,
"closedAt": 1607031646361,
"incidentSolver": {
"incidentId": null,
"userName": "DUDE"
},
"user": {
"userId": null,
"userType": "USER",
"userName": "USERNAME"
},
"changeId": "StringID",
"changeType": "INSTALLATION_OF_OS",
"description": "SOME TEXT"
}
所以我创建了另一个对同一个 URL 的 POST 请求,它成功了!!!所以现在我知道,如果我创建一个与我的 objectt(及其父超类)不对应的错误 JSON,我会收到这种错误。非常感谢
推荐阅读
- electron - 防止电子应用程序关闭,直到清理完成
- python - 哪个是熊猫的 R which() 的替代品?
- python - 在 Matplotlib 中绘制多个子图时如何去除白线?
- c++ - std::bind c++ in if 语句
- acumatica - 即使在货件状态得到确认、完成或开票后,有人可以帮助我如何在货件屏幕上编辑和保存跟踪号字段
- node.js - Visual Studio Code & nodejs
- r - R:按因子绘制两个单独列的平均值
- mysql - 如何使用表的返回结果访问数据库?
- microsoft-graph-api - 有没有办法使用 IGraphServiceClient 批量删除文件的所有权限?
- python - 正则表达式从字符串中解析产品尺寸(计数、包装尺寸)