StormeHawke
StormeHawke

Reputation: 6207

Spring JSR303/349 validation without @Validated

I'm generating POJOs from json schema using jsonschema2pojo. I want to use the jsr303/349 bean validation stuff. I added the necessary items to the classpath, added the necessary beans to trigger the validation, however jsonschema2pojo does not add @org.springframework.validation.annotation.Validated to the generated classes and so the validation doesn't get triggered when a request comes into my spring boot application.

I was able to confirm that my validator is set up correctly by writing an empty class like so and changing the @RequestBody type to the new type:

@Validated
class SomeClass extends SomeGeneratedClass {

}

When I did so validation works as expected. However, we're looking at dozens if not potentially a hundred or more of these extension objects and having a bunch of those is the epitome of wet (IE, not DRY) code and so this is less than ideal as a solution.

So my question is: Is there a way to trigger bean validation in spring on an incoming request if the object in question is not annotated with @Validated? Note that jsonschema2pojo does not have a dependency currently on Spring and I find it unlikely the author would accept a pull request that adds one.

-- Code if it's helpful

An example JSON schema:

{
  "type": "object",
  "properties": {
    "userIds": {
      "type": "array",
      "items": { "type": "string" },
      "minSize": 1
    }
  },
  "additionalProperties": false,
  "required": ["userIds"]
}

Generated class:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
    "userIds"
})
public class ExampleSchema {

    /**
     * 
     * (Required)
     * 
     */
    @JsonProperty("userIds")
    @Valid
    @NotNull
    private List<String> userIds = new ArrayList<String>();

    /**
     * 
     * (Required)
     * 
     */
    @JsonProperty("userIds")
    public List<String> getUserIds() {
        return userIds;
    }

    /**
     * 
     * (Required)
     * 
     */
    @JsonProperty("userIds")
    public void setUserIds(List<String> userIds) {
        this.userIds = userIds;
    }

    public ExampleSchema withUserIds(List<String> userIds) {
        this.userIds = userIds;
        return this;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(userIds).toHashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        if ((other instanceof ExampleSchema) == false) {
            return false;
        }
        ExampleSchema rhs = ((ExampleSchema) other);
        return new EqualsBuilder().append(userIds, rhs.userIds).isEquals();
    }
}

Validation bean setup in my WebConfig:

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(LocalValidatorFactoryBean validator) {
        final MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
        methodValidationPostProcessor.setValidator(validator);

        return methodValidationPostProcessor;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }

And my controller method:

@RequestMapping(value = "", method = RequestMethod.POST)
public void postExample(@RequestBody @Valid ExampleSchema example) {
    //Perform actions on validated object
}

Upvotes: 2

Views: 1174

Answers (3)

Chay
Chay

Reputation: 57

create an initbinder in your controller like this:

@Autowired
YourValidator validator;

@InitBinder()
protected void initBinder(WebDataBinder binder) {

    binder.setValidator(validador);

}

And in your endpoint

public OrdenServicioTo guardarOrden(@ModelAttribute("dto")@Validated @RequestBody Object dto)
{
    ///YOurCode
}

Upvotes: 0

reos
reos

Reputation: 8324

In your class you're defining and instantiating the array in the same line.

private List<String> userIds = new ArrayList<String>();

So, when Spring validate the object, the List userIds is not null.

You have two options here.

  1. You can remove the instantiation of the userIds.

    private List userIds;

  2. You can change the validation. Instead of using @NotNull you can use @Size(min=1)or you can use they both. Also you can use @NotEmpty but you need the hibernate validator.

Upvotes: 1

Arpit Aggarwal
Arpit Aggarwal

Reputation: 29276

You can annotate Controller or Service with @Validated rather than doing it for all objects that need to be validated, as:

@Validated
@Controller
public class Controller {

    @RequestMapping(value = "", method = RequestMethod.POST)
    public void postExample(@RequestBody @Valid ExampleSchema example){
       //Perform actions on validated object
    }
}

Upvotes: 0

Related Questions