How can I enable logging of Rest Api requests for incorrect data conversion in the request - spring

I have an endpoint that accepts #ResponseBody.
But when I sent the data, I could not understand why the status 400 was coming to me in response. There was nothing in the logs that could tell me.
build.gradle
plugins {
id 'org.springframework.boot' version '2.7.5'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
{
...
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.fasterxml.jackson.core:jackson-annotations'
implementation 'org.flywaydb:flyway-core'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-validation'
....
}
controller
#PostMapping(value = "new")
public ResponseEntity<TDto> addNew(#Valid #RequestBody TDto dto) {
...
}
application.yml
logging:
level:
org:
springframework:
web:
filter:
CommonsRequestLoggingFilter: DEBUG
util:
ContentCachingRequestWrapper: DEBUG
ContentCachingResponseWrapper: DEBUG
management:
endpoints:
web:
exposure:
include: httptrace
I have the dto:
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#ToString
public class TDto {
private Long id;
.....
#JsonSerialize(using = LocalDateTimeSerializer.class)
#JsonProperty("t_date")
private LocalDateTime tDate;
It turned out that jackson can't convert a date in this format
"2022-10-22 10:17:37.402150 +00:00"
but it works with a date of this format
"2021-08-02T03:00:00"
To make it work, I had to do this:
#JsonProperty("t_date")
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime tDate;
and the date has to be transmitted strictly in this format"
"2021-09-15T10:15:37.93456"
or
"2021-09-17T10:14:37"
and how to set up different formats is not understood.
For example, I can't use time variables in Postman
"t_date": {{$timestamp}},
since in this case, its own date format is generated (also timestamp).
But the worst thing is that I don't see any errors in the logs.
They just don't exist.
In addition, how in general can you configure several Jackson date serializers and specify that Jackson automatically selects the appropriate one, and if it does not find it, then throws the user exception?
This error was not in close visibility, how to enable logging of such problems?

To use custom format you can use annotation like this:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private LocalDateTime tDate;
For more details see this question and its accepted answer: Spring Data JPA - ZonedDateTime format for json serialization

Related

POJO to Entity mapping with ModelMapper

My POJO has a variable annotated like this
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSxxx")
#JsonDeserialize(using = ZonedDateTimeDeserializer.class)
private ZonedDateTime startTs;
//other private variables
My goal is to map this to an Entity for which I'm using ModelMapper and the sample code goes like this
public POJOEntity mapToPOJOEntity(POJO pojo) {
return modelMapper.map(pojo, POJOEntity.class);
}
After mapping when I look at the startTs variable in debug mode it has the desired format but when I check in the db startTs is saving as a whole ZonedDateTime object rather than the desired format I specified in POJO. Did anyone face this issue if yes can someone please help?
Value saved in cosmosDB:
Expected output: "startTs": "2023-01-12T08:58:32.452-06:00"
PS: CosmosDB, spring-boot, Java8, ModelMapper version: 2.3.8

Validate Bean in Spring boot

Is there a way in spring boot to validate properties in a bean? For Example, consider an Employee Bean consisting of following properties -
id - must start with 01,02,22
Department - Should be any of the one - D1, D2, D3
Name - Must not contain any digits and max length of 10 characters.
I can have a separate method and validate the bean every time but looking for some better way to implement this using spring boot.
You could use spring boot validation to validate your patterns.
Add this dependency to your gradle file implementation('org.springframework.boot:spring-boot-starter-validation') https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation checkout the latest version
class Employee {
#Pattern(regexp = "^(01|02|22).+$")
private String id;
#Size(max = 10)
#Pattern(regexp = "^[^0-9]+$")
private String name;
#Pattern(regexp = "^D[1-3]$")
private String department;
}
And in your request
#RestController
class EmployeeRequest {
#PostMapping("/registerEmployee")
ResponseEntity<String> registerEmployee(#Valid #RequestBody Employee employee) {
return ResponseEntity.ok("valid");
}
}
Note: I am not sure about syntax of regex but you should define your regex for your business requirements.

Failed to convert 1985-04-12T23:20 into java.util.Date

[Spring + Kotlin]
These are the dependencies:
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-rest")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-web-services")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
This is the Entity:
#Entity
class MatchEntity(
#Id #GeneratedValue val id: Long,
#NotBlank val matchDateTime: Date,
#NotBlank #ManyToOne #JoinColumn val tournamentInvolved: TournamentEntity
)
Whenever I try to run the following query:
interface MatchRepository : JpaRepository<MatchEntity, Long> {
fun findMatchesByMatchDateTimeIsAfter(matchDateTime: Date)
}
with a test string like so 1985-04-12T23:20, I get the error:
QueryMethodParameterConversionException: Failed to convert 1985-04-12T23:20 into java.util.Date!
I tried, as suggested here, with patterns like #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) and #DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") in the signature of the query method, without solving.
Also, as suggested here, I tried adding
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0") to the dependencies
spring.jackson.serialization.write_dates_as_timestamps=false to the application.properties.
Didn't work.
UPDATE:
I also tried with LocalDateTime and Instant classes. Still getting the same Exceptions:
QueryMethodParameterConversionException: Failed to convert 1985-04-12T23:20 into java.time.LocalDateTime!
QueryMethodParameterConversionException: Failed to convert 1985-04-12T23:20 into java.time.Instant!
Solved
Using #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") worked.

spring boot, jackson and localdate

I use spring boot with mysql
in my application.properties
spring.jpa.generate-ddl=true
spring.jackson.serialization.write-dates-as-timestamps=false
In my build.gradle I have
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-data-rest')
compile('org.springframework.boot:spring-boot-starter-web')
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
In my java class
import java.time.LocalDate;
#Entity
public class WebSite implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long webSiteId;
private LocalDate date;
...
}
When this table is created,
date field is created like a TINYBLOB
Why is not a date
This is not an issue with Jackson, but rather that whatever you are using for ORM doesn't know how to convert a Java LocalDate to a MySQL Date.
There are two ways to do this. If you are using Hibernate, you simply include org.hibernate:hibernate-java8 in your dependencies.
Alternatively, if you want just use JPA, you need to create an Attribute Converter. For example:
#Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {
#Override
public Date convertToDatabaseColumn(LocalDate locDate) {
return (locDate == null ? null : Date.valueOf(locDate));
}
#Override
public LocalDate convertToEntityAttribute(Date sqlDate) {
return (sqlDate == null ? null : sqlDate.toLocalDate());
}
}
The Attribute Converter will handle converting between a Java LocalDate and MySQL Date.
See: http://www.thoughts-on-java.org/persist-localdate-localdatetime-jpa/

BeanPropertyRowMapper does not understand joda time types anymore since upgrading to spring boot 1.4 / spring cloud camden

I have a Spring Batch Job that defines a JdbcPagingItemReader with a BeanPropertyRowMapper :
JdbcPagingItemReader<RawNotice> reader = new JdbcPagingItemReader<>();
final SqlPagingQueryProviderFactoryBean sqlPagingQueryProviderFactoryBean = new SqlPagingQueryProviderFactoryBean();
sqlPagingQueryProviderFactoryBean.setDataSource(dataSource);
sqlPagingQueryProviderFactoryBean.setSelectClause("select *");
sqlPagingQueryProviderFactoryBean.setFromClause("from a_table");
sqlPagingQueryProviderFactoryBean.setWhereClause("state = :state");
sqlPagingQueryProviderFactoryBean.setSortKey("id");
Map<String, Object> parameters = new HashMap<>();
parameters.put("state", "interesting_state");
reader.setQueryProvider(sqlPagingQueryProviderFactoryBean.getObject());
reader.setDataSource(dataSource);
reader.setPageSize(10);
// The line below is the interesting one
reader.setRowMapper(new BeanPropertyRowMapper<>(MyEntity.class));
reader.setParameterValues(parameters);
return reader;
This used to work fine, but since we upgraded to spring boot 1.4 and spring cloud Camden, it throws an exception :
org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type [java.sql.Timestamp] to required type [org.joda.time.LocalDateTime] for property 'ADateColumn'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.sql.Timestamp] to required type [org.joda.time.LocalDateTime] for property 'ADateColumn': no matching editors or conversion strategy found
The column ADateColumn is declared as a Joda LocalDateTime and stored as a java.sql.Timestamp in the database.
I'm quite aware I could add my own joda converters to the BeanPropertyRawMapper conversionService for example, or create a PropertyEditor that understands Java LocalDateTime, but that looks rather like a configuration problem, like something isn't being registered right.
Anybody with a solution/suggestion to fix this problem ?
Thanks !
This is the part of the entity that poses problem :
#Entity
#EqualsAndHashCode(of = { "..." })
#ToString(of = { .... })
public class MyEntity {
#Getter
#Setter
#Id
#GeneratedValue
private Long id;
#Getter
#Version
#Column(nullable = false)
private int version;
//<--- snip --->
#Getter
#Setter
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime aDateColumn;
}
Hibernate is version 4.3.11.Final
JPA is version 2.1 with Hibernate Entity Manager 4.3.11.Final
So I finally ended up creating my own BeanPropertyRowMapper with custom Joda converters :/

Resources