Home > Net >  springboot server returns error 400 after post request using postman
springboot server returns error 400 after post request using postman

Time:11-10

whenever I try to send this over postman:

{
    "date": "2021-11-05 12:32:32",
    "start": "start",
    "destination": "destination",
    "provider": "provider",
    "driver":1,
    "vehicule":1
}

i get an error 400, bad request, i'm using both the @restController and @requestBody annotations while also setting the content type to json.

i get this error on the debugger:

2021-11-09 16:57:52.086  WARN 11748 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2021-11-06 12:32:32.0": not a valid representation (error: Failed to parse Date value '2021-11-06 12:32:32.0': Cannot parse date "2021-11-06 12:32:32.0": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2021-11-06 12:32:32.0": not a valid representation (error: Failed to parse Date value '2021-11-06 12:32:32.0': Cannot parse date "2021-11-06 12:32:32.0": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
 at [Source: (PushbackInputStream); line: 3, column: 17] (through reference chain: com.siam.HRAssistTool.Entity.Schedule["date"])]

I don't understand how i should fix this what i assume date format related issue

when I remove the time from the json body and only leave the date, I get this error:

2021-11-09 17:34:55.418  WARN 11748 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.siam.HRAssistTool.Entity.Vehicule` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.siam.HRAssistTool.Entity.Vehicule` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1)
 at [Source: (PushbackInputStream); line: 8, column: 20] (through reference chain: com.siam.HRAssistTool.Entity.Schedule["vehicule"])]

my schedule entity:

@Entity
public class Schedule implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id ; 
    private Date date ; 
    private String Start; 
    private String destination;
    @OneToOne( fetch = FetchType.LAZY)
    private Staff driver; 
    @OneToOne(fetch = FetchType.LAZY)
    private Vehicule vehicule; 
    private String provider;
    //constructors, getters and setters 
}

my controller :

@RestController
public class ScheduleController {

    @Autowired
    ScheduleService scheduleService; 

    @PostMapping(value="/schedule/create")
    public @ResponseBody String createSchedule( @RequestBody Schedule schedule) {
        System.out.println(schedule.toString());

        return scheduleService.addSchedule(schedule);
        
    }
//other crud operation
}

CodePudding user response:

First of all, replace Date with LocalDate, which is part of the new Java Time API. with this you can configure Jackson to handle serialization and deserialization of such a complex type easily. Add the following dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.11.0</version>
</dependency>

And then configure Jackson accordingly:

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        objectMapper.registerModule(javaTimeModule);

    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        return objectMapper;
    }
}

Then, please avoid using Entities in your Controller, either as a response or request type. Instead, use DTOs which are specific representations for your core model Entities.

public class ScheduleCreationDto {
    private LocalDate date; 
    private String Start; 
    private String destination;
    private Long driverId;      // I am guessing the ID is a Long
    private Long vehiculeId;    // I am guessing the ID is a Long
    private String provider;
    
    //constructors, getters and setters 
}

This should now be used as the request body:

@RestController
public class ScheduleController {

    @Autowired
    ScheduleService scheduleService; 

    @PostMapping(value="/schedule/create")
    public @ResponseBody String createSchedule(@RequestBody ScheduleCreationDto scheduleCreationDto) {
        return scheduleService.addSchedule(schedule);
    }
    
    //other crud operation
}

You also need to change ScheduleService so that it creates a Schedule based on ScheduleCreationDto. Most of the properties require a simple mapping, but others (driverId and vehiculeId) requires you to actually get those Entities from the Database using the provided ID. Something similar to the following should be done in your ScheduleService:

@Service
public class ScheduleService {

    @Autowired
    ScheduleRepository scheduleRepository; 

    @Autowired
    DriverRepository driverRepository; 

    @Autowired
    VehiculeRepository vehiculeRepository; 

    public String addSchedule(ScheduleCreationDto scheduleCreationDto) {
        Driver driver = driverRepository.findById(scheduleCreationDto.getDriverId());
        Vehicule vehicule = vehiculeRepository.findById(scheduleCreationDto.getVehiculeId());

        Schedule schedule = new Schedule(scheduleCreationDto.getDate(), scheduleCreationDto.getStart(), 
            scheduleCreationDto.getDestination(), driver, vehicule, scheduleCreationDto.getProvider());
        scheduleRepository.save(schedule);    
    
        return // whatever String you want to return, you should actually return the created Schedule, but that is a different topic
    }

    //other crud operation
}
  • Related