1

Using Spring Boot 2.2.6, given this Controller:

@RestController
@Validated
class MyController {
    @GetMapping("foo")
    fun getFirmwareVersionDifference(
        @RequestParam @Valid versions: @Valid Map<
            @Pattern(regexp = "a|b|c")
            String, String>
    ): String {
       // …
    }
}

I would expect the request foo?d=any to throw a ConstraintViolationException, but the controller is called normally.

I have a MethodValidationPostProcessor in my context and it works for other validations. If I change the RequestParam to @RequestParam @Valid @Length(min=3) installedFirmwareVersion: String it works as expected fine.

What am I missing?

2 Answers 2

2

This is a kotlin compiler bug / missing feature: https://youtrack.jetbrains.com/issue/KT-13228

As documented in https://youtrack.jetbrains.com/issue/KT-35843 this is partially solved in kotlin 1.3.70.

Here's how you can set the compiler flag in build.gradle.kts

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xemit-jvm-type-annotations")
        jvmTarget = "11" // at least 8 should work, I only tested 11
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Still doesn't work for me, at least in the tests code is still not validating. Kotlin version is 1.4.21. Works only with @field:..
1

Maybe my answer is not a correction to the one you are looking for but it certainly does checks the validation you are trying to do in your controller.

import in.silentsudo.validators.annotations.RequestMap;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Map;

public class MapParamValidator implements ConstraintValidator<RequestMap, Map<String, String>> {

    @Override
    public boolean isValid(Map<String, String> stringStringMap, ConstraintValidatorContext constraintValidatorContext) {
        return stringStringMap.containsKey("a") || stringStringMap.containsKey("c") || stringStringMap.containsKey("b");
    }
}

import in.silentsudo.validators.MapParamValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = {MapParamValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMap {
    String message() default "Invalid Request Map";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Controller implementation:

@GetMapping("foo")
    public String getFirmwareVersionDifference(
            @RequestParam @Valid
            @RequestMap
                    Map<String, String> versions) {

        return "OK";
    }

Please make sure to add @Validated on controller like this:

@RestController
@Validated
@RequestMapping("/somepath")
public class StatusController {
...
}

If everything works well, you should get something like this

There was an unexpected error (type=Internal Server Error, status=500).
getFirmwareVersionDifference.versions: Invalid Request Map
javax.validation.ConstraintViolationException: getFirmwareVersionDifference.versions: Invalid Request Map
...

3 Comments

Thanks for your answer! After half a day I just found the problem: it's a kotlin compiler bug/missing feature youtrack.jetbrains.com/issue/KT-13228
Plain vanilla java is awesome!
Would have been my choice too ;)

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.