spring request parameter is nested json validation fail - java

enter image description hereI have a request like this:
#Controller
public class Test {
#ResponseBody
#PostMapping("/s")
public ResData test(#RequestBody #Valid ResData resData){
System.out.println(resData);
return resData;
}
}
#Data
class ResData{
#NotNull
#Valid
Message MSG;
int code;
}
#Data
class Message {
#NotBlank(message = "s1 cannot be blank.")
String s1;
String s2;
}
but when I test it with post some data like this:
{
"MSG":{
"s1":" ",
"s2":"ss2"
},
"code":"200"
}
it just cannot recognize:
{
"code": 10004,
"msg": "参数校验失败",
"exceptionMsg": "校验失败:MSG:must not be null, ",
"body": null
}
However, when I change the "MSG" to lowercase "msg", it just worked.
But my situation need the paramter come here is in Uppercase,
could someone tell me the truth?
thank you very much!!

I would recommend lowercase of property naming, but you can use #JsonProperty annonation to tell Jackson actual Json property is uppercase
#Data
class ResData{
#NotNull
#Valid
#JsonProperty("MSG")
Message msg;
int code;
}

Related

Spring doesn't validate JSON request

When I send the request:
"Person": {
"name": 5
}
The request should fail (bad request) because 5 isn't a String. It prints: Person{name='5'}.
Similarly, there's no error when I send null.
I have these annotations:
#JsonProperty("name")
#Valid
#NotBlank
private String name;
Controller:
public void register(#Valid #RequestBody Person p) {
...
}
How can I make it validate the name so only strings are accepted?
Add a BindingResult parameter.
public void register(#Valid #RequestBody Person p, BindingResult result) {
if (result.hasErrors()) {
// show error message
}
}
How can I make it validate the name so only strings are accepted?
Use the #Pattern annotation.
#JsonProperty("name")
#Valid
#NotBlank
#Pattern(regexp="^[A-Za-z]*$", message = "Name should contains alphabetic values only")
private String name;
For more details check this link and this one for the regex.

Field lastName will not be returned from server if annotated with Builder.ObtainVia(method = "getLastName")

Field lastName will not be returned from server if annotated with Builder.ObtainVia(method = "getLastName").
I am using spring and lombok.Builder.ObtainVia(https://projectlombok.org/features/Builder).
I have a class Person with field private String lastName, and annotated with #Builder.ObtainVia(method = "getLastName")
// model
#Builder(toBuilder = true)
#Data
public class Person {
private int age;
private String name;
#Builder.ObtainVia(method = "getLastName")
//#JsonProperty("last_name")
private String lastName;
private String getLastName() {
return "James";
}
}
// rest controller
#RestController
public class Hello {
#GetMapping
public Person hello() {
return Person.builder().age(11).name("hoyt").build().toBuilder().build();
}
}
problem:
When I connect to server http://localhost:8080 and get the response of a Person object,
expected result :
{
"age": 11,
"name": "hoyt",
"last_name": "James"
}
actual result:
{
"age": 11,
"name": "hoyt"
}
what I find
If I put another annotation #JsonProperty("last_name") on it, lastName will be returned
More strange, if I change the field name from lastName to anything else, the field will be returned. For example, if change the field to dob, then it will be return.
I am really confused about why I can`t name the field lastName.
I get the feeling that it has something to do with Json or lombok.
Can you explain why this happened? Thanks in advance

I can't catch NumberFormatException in spring boot api rest

this problem has me crazy and I don't know where to look anymore.
I have a rest api that receives a #RequestBody DTO
public ResponseEntity<JuntaCalificadoraDTO> edit(#Valid #RequestBody JuntaCalificadoraDTO juntaCalifDTO) {
......
This is the DTO that I receive and that I validate with java bean validations. Generate getter and setters with lombok
#Data
#NoArgsConstructor
public class JuntaCalificadoraDTO extends RepresentationModel<JuntaCalificadoraDTO> {
private Long id_process;
#NotNull #Min(1) #Positive
private Integer presidentJC;
#NotNull #Min(1) #Positive
private Integer secretaryJC;
#NotNull #Min(1) #Positive
private Integer representativeFuncJC;
}
Java bean validations does its job. It is valid that it is not zero, its minimum and also that it is positive. The problem is that it does not validate when I pass a letter to a variable, for example:
{
"id_process": 4455,
"presidentJC": "dd",
"secretaryJC": 33,
"representativeFuncJC": 3
}
It detects the error, and postman returns "400 Bad Request" but nothing else. Also the Intellij console doesn't show anything, no stack.
I need to catch that error which I imagine is a "NumberFormatException" but I haven't been able to. And I don't know why he hides it. I created a method in a #ControllerAdvice class but no success either.
#ExceptionHandler (value = {NumberFormatException.class})
public final ResponseEntity<Object> invalidNumberHandling(NumberFormatException ex) {
ApiError apiError = ApiError.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.BAD_REQUEST)
.message("Number Format Exception")
.errors(List.of("El o los parámetros de entrada no son válidos"))
.details(ex.getMessage())
.build();
return new ResponseEntity<>(apiError, apiError.getStatus());
}
I will appreciate any guidance. And sorry for my bad english
You are close. It's actually an InvalidFormatException that is wrapped into a HttpMessageNotReadableException.
By catching the InvalidFormatException you have access to the field that failed and to the wrong value, so it should be enough for you create a meaningful response to the user.
See this sample application (Java 17) in case you have any doubts - if this doesn't work please let me know your Spring Boot version.
#SpringBootApplication
public class SO72312634 {
public static void main(String[] args) {
SpringApplication.run(SO72312634.class, args);
}
#Controller
static class MyController {
#PostMapping
public ResponseEntity<?> edit(#Valid #RequestBody JuntaCalificadoraDTO juntaCalifDTO) {
return ResponseEntity.ok().build();
}
}
#Bean
ApplicationRunner runner() {
return args -> new RestTemplate().exchange(RequestEntity
.post("http://localhost:8080")
.contentType(MediaType.APPLICATION_JSON)
.body("""
{
"id_process": 4455,
"presidentJC": "ds",
"secretaryJC": "abc",
"representativeFuncJC": 3
}
"""), String.class);
}
#ControllerAdvice
static class ExceptionAdvice {
#ExceptionHandler(value = InvalidFormatException.class)
public final ResponseEntity<Object> httpMessageNotReadable(InvalidFormatException ex) {
String failedPaths = ex.getPath().stream().map(JsonMappingException.Reference::getFieldName).collect(Collectors.joining());
return new ResponseEntity<>("Field %s has invalid value %s ".formatted(failedPaths, ex.getValue()), HttpStatus.BAD_REQUEST);
}
}
#Data
#NoArgsConstructor
public static class JuntaCalificadoraDTO {
private Long id_process;
#NotNull
#Min(1) #Positive
private Integer presidentJC;
#NotNull #Min(1) #Positive
private Integer secretaryJC;
#NotNull #Min(1) #Positive
private Integer representativeFuncJC;
}
}
Output:
Caused by: org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : "Field presidentJC has invalid value ds "

Micronaut nested Json input Validation

How do you validate Json Body
{
"name": "good student",
"marks": {
"math": "122",
"english": "12"
}
}
This Doesn't works, It accepts with or without marks in JSON body, even if #NotNull etc are added to marks in Student DTO
#Introspected
public #Data class Student {
#NotBlank
private String name;
#Valid
#JsonProperty("marks")
private Marks marks;
#Introspected
static #Data class Marks{
#NotBlank
private String math;
#NotBlank
private String english;
}
}
Controller Annotated with #Validated
Method param annotated with #Valid #Body
This works for me in Micronaut version 2.0.3:
#Introspected
public #Data class Student {
#NotBlank
private String name;
#Valid
#NotNull
private Marks marks;
#Introspected
static #Data class Marks{
#NotBlank
private String math;
#NotBlank
private String english;
}
}
Field marks should be annotated by:
#NotNull - to tell the validator that it must be present
#Valid - to tell the validator that it must validate nested fields
Example controller looks like this:
#Validated
#Controller("/students")
public class StudentController {
#Post
public void create(#Valid #Body Student student) {
// do something
}
}
Tested by curl:
curl -v -X POST http://localhost:8080/students -H 'Content-Type: application/json' -d '{"name":"John"}' | jq
With this response:
{
"message": "student.marks: must not be null",
"_links": {
"self": {
"href": "/students",
"templated": false
}
}
}

java jpa json standard

First, thank you very much for reading this question.
I have a JPA project and everything works fine, the json that i get with the controller is of this form:
{"id": 1, "name": "Canada"},{"id": 2, "name": "USA"}
All its fine but i would like to get a json with the Jsend standard, it something like this:
{
status : "success",
data : {
"country" : [
{"id": 1, "name": "Canada"},
{"id": 2, "name": "USA"}
]
}
}
{
"status" : "fail",
"data" : { "title" : "A title is required" }
}
{
"status" : "error",
"message" : "Unable to communicate with database"
}
As you can see i want to have a status that says success, fail or error:
But i dont know how to do it. This is my DTO, DAO and Controller
#Entity
public class Country implements Serializable {
private static final long serialVersionUID = -7256468460105939L;
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name;
//Constructor, get and set
DAO
#Repository
#Transactional
public class CountryRepository {
#PersistenceContext
EntityManager entityManager;
public CountryDTO findById(int id) {
return entityManager.find(CountryDTO.class, id);
}
}
Controller
#RestController
public class CountryController {
#Autowired
CountryDTO repository;
#RequestMapping(value="api/country/{id}", method=RequestMethod.GET)
public #ResponseBody CountryDTO getByID(#PathVariable("id") int id){
return repository.findById(id);
}
}
Again thank you for your time.
Its very good question from my point of view. So I can give list of action items to achieve this.
You should aware of #ControllerAdvice annotation which is available in Spring.
By utilizing that you can play with your response object.
Then You should create your own Object which is similar to JSend. In my case, I have created JSendMessage class
public class JSendMessage {
String status;
String message;
Object data;
// Add getter and setter
}
Now you should map above class with your #ControllerAdvice return your required object.
So whenever there is a exception you can create and send your own custom exception message.
There will be lot of reference for this. Just look for #ControllerAdvice

Categories