I have created a simple web REST application that has to save a “change ticket” to database. But after I try to create a POST request with JSON as body I get an error:
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
I am using Spring Boot 2, MySQL as DB and MapStruct to convert DTO to entities and vice versa. Also I use Project lombok to get rid of some boilerplate code
My controller POST method is depicted here:
@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");
}
}
My entity which I am saving looks like this:
@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;
}
Superclass:
@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;
}
And here is the sample JSON code I am sending via POSTMAN to as POST request to: 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"
}
]
}
I don’t know if it is a problem with Jackson not knowing how to deserialize/serialize child and parent objects or If I am building my Json wrongly. If you will need to see my repositories of service classes I will edit this post to show them.
EDIT: adding 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;
}
BaseItemDTO
@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;
}
Thanks for help guys
Advertisement
Answer
I am back with a solution that worked for me. I have tried to test and see what can cause this problem and it turned out, that this problem occurs when you construct your JSON request body wrong.
I was sending POST request to my controller with JSON body as you can see up in my post. But than I got the idea to write a test method that will transform my object to JSON so I will be sure that my JSON is constructed in a proper way. You can see it here:
@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);
}
}
This method returned a simple JSON string that looked like this:
{
"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"
}
So I have created another POST request to the same URL and it worked!!! So now I know that if I will create a bad JSON that is not corresponding to my objectt(and its parent super class) I will get this kind of error. Thanks a lot