1

I am working on Kotlin with spring boot.

I have an entity Hours

data class Hours(
         @get: Max(value=3) val value : Long)

And in my constructor, I have the following

fun postHours(@RequestBody @Valid hours: @Valid LinkedHashMap<String,  Array<Hours>>): String {
        return service.addHours(hours)
    }

But the validation is not working at all.

I am able to send requests with value > 3 even though I have set the max value as 3. and there is no error.

Could someone tell me whats wrong here?

2
  • Use Array<@Valid Hours> Commented Apr 17, 2020 at 13:05
  • Tried it. Didnt work. Commented Apr 17, 2020 at 15:44

2 Answers 2

1

I have a similar case that is working in Java (you can rewrite to Kotlin). Please try :

define a new bean in your configuration class annotated with @Configuration :

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
}

annotate your controller with :

@RestController
@Validated

Then you can validate you class with :

postHours(@RequestBody LinkedHashMap<String, @Valid Hours> hours)

----- MODIFICATION TO PROVIDE FULL EXAMPLE -----

You can use this full example. I am using : spring-boot-starter-web 2.2.6 with lombok.

Here is the class to validate :

@Data
public class Foo {
    @Max(value = 3)
    private Integer count;
}

Controller :

@RestController
@Validated
public class FooController {
    @PostMapping("/validate")
    public String validate(@RequestBody LinkedHashMap<String, List<@Valid Foo>> foos) {
        return "foo";
    }
}

Configuration class :

@Configuration
public class AppConfig {
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

Controller advice (to send exception as json) :

@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
    @Data
    @AllArgsConstructor
    class ApiError {
        private HttpStatus status;
        private String message;
        private List<String> errors;
    }

    @ExceptionHandler({ ConstraintViolationException.class })
    public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
        List<String> errors = new ArrayList<>();
        for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.add(violation.getRootBeanClass().getName() + " " +
                    violation.getPropertyPath() + ": " + violation.getMessage());
        }

        ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
    }
}

Test class :

@WebMvcTest(FooController.class)
public class FooControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void test() throws Exception {
        LinkedHashMap<String, Foo> foos = new LinkedHashMap<>();
        Foo foo = new Foo();
        foo.setCount(4);
        foos.put("foo", foo);
        this.mockMvc.perform(post("/validate")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(bars)))
                .andExpect(status().isBadRequest())
                .andExpect(content().string(containsString("validate.foos[foo].<map value>[0].count: must be less than or equal to 3")));
    }
}

The unit test shows that validation inside linkedhashmap is working.

Sign up to request clarification or add additional context in comments.

14 Comments

i provided a full tested example in my post. please tell me if it works.
This will work when the value of Map is an Object, but in my case, the value part of Map is an Array.
my fault i forgot this fact. i modified my post (controller and test) to validate a list. take a look on FooControllerTest at the last line.
I have everything except the controller advice, not working. Do I need to add the controlleradvice?
Without controller advice it will throw a raw constraintvalidationexception as a response without formatting it to json. Yes you need it too. What version of spring boot do you use? Did you add spring-boot-starter-web to your pom.xml?
|
0

Finally found a fix for this. Wrote an Array wrapper class

   class ArrayWrapper<E>( elements: Array<E>?) {

        @Valid var _array: Array<E>? = elements

        override fun equals(other: Any?): Boolean {
            return if (other !is ArrayWrapper<*>) {
                false
            } else Arrays.equals(_array, other._array)
        }

        override fun hashCode(): Int {
            return Arrays.hashCode(_array)
        }
    }

and then modified the controller method like this.

fun postHours(@RequestBody @Valid hours: LinkedHashMap<String, ArrayWrapper<Hours>>): String {
        return service.addHours(hours)
    }

Now validation works perfectly!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.