mark951131b
mark951131b

Reputation: 475

@Valid not working with JAX-RS using Spring Boot

I just can't get validations to work. I have this simple endpoint that is part of an Spring Boot application:

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(@Valid UserDTO userDTO, @Context UriInfo uriInfo) {
    User user = UserParser.parse(userDTO);
    userService.save(user);
    final URI uri = uriInfo.getAbsolutePathBuilder().path(String.valueOf(user.getId())).build();
    return Response.created(uri).build();
}

Then, the UserDTO to validate:

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDTO {

    private Long id;

    // redundant validations, for testing
    @NotNull
    @Size(min = 5, max = 80, message = "First name too short")
    @NotBlank(message = "First name blank")
    @NotEmpty(message = "First name empty")
    private String firstName;

    @NotNull
    @Size(min = 2, max = 80, message = "Last name too short")
    private String lastName;

    @NotNull
    private String email;

}

And it always processes any request, even with empty fields. I even created a test endpoint to see if the problem was having the validations inside the DTO

@Path("/validator/{testInt}")
@GET
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String validator(@Valid @Min(value = 30, message = "testInt too small") @PathParam("testInt") Integer testInt) {
    return String.valueOf(testInt);
}

But that doesn't work either, it happily returns any int it receives. In case it matters, my endpoint is a @Service, and here is the relevant parts of my maven dependency tree:

[INFO] +- org.glassfish.jersey.ext:jersey-bean-validation:jar:2.22.1:compile
[INFO] |  +- org.glassfish.hk2.external:javax.inject:jar:2.4.0-b31:compile
[INFO] |  +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] |  +- org.hibernate:hibernate-validator:jar:5.2.2.Final:compile
[INFO] |  |  +- org.jboss.logging:jboss-logging:jar:3.3.0.Final:compile
[INFO] |  |  \- com.fasterxml:classmate:jar:1.1.0:compile
[INFO] |  +- javax.el:javax.el-api:jar:2.2.4:compile
[INFO] |  \- org.glassfish.web:javax.el:jar:2.2.4:compile

I also set breakpoints inside HibernateValidator, and saw that two of its methods get called, so looks like it's running. Just not validating.

EDIT: My jersey configuration

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(RequestContextFilter.class);
        register(LoggingFilter.class);
        register(JacksonFeature.class);
        packages("com");
    }
}

Upvotes: 9

Views: 9021

Answers (5)

ThirstForKnowledge
ThirstForKnowledge

Reputation: 1284

The question was raised years ago, but for all using Stack Overflow as knowledge database, I want to record an additional solution thats works for me.

Despite contrary documentations I had to annotate the controller class with @Validated and use @Valid at method level in a Spring Boot (v2.1.3.RELEASE) with Hibernate (5.3.7.Final) and Hibernate Validator (6.0.14.Final) context. If one of both annotations was missing, I got also the above mentioned error scenario.

@RestController
@RequestMapping("/users")
@Validated
public class UserController {

  @RequestMapping(value = "/create", method = RequestMethod.POST)
  public User saveUser(@RequestBody @Valid UserDto user) {
    <your service call>
  }
}

Upvotes: 3

user2964500
user2964500

Reputation:

Add the following configuration to your JerseyConfig method

public JerseyConfig()
{
    register(YourEndpoint.class);
    property(org.glassfish.jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}

Upvotes: 1

Bharath
Bharath

Reputation: 1807

ValidationFeature is auto discoverable as per jersey 2.0. You don't have to explicitly register it.

Include the flag, property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true) in jersey resource config for the validation message to be sent in the response.

Make sure Jersey-bean-validation.jar is on the classpath (in your case it already is).

Simple Example,

//Web Resource
@Path("/accounts")
public Object getaccount(@Valid @BeanParam AccountRequest);

public class AccountRequest {
@NotNull
private String accountId;
} 

refer, https://jersey.java.net/documentation/latest/bean-validation.html

Upvotes: 0

WeMakeSoftware
WeMakeSoftware

Reputation: 9162

In case it matters, my endpoint is a @Service

Try to set the stereotype to @Controller

Check if this bean is defined in your Web configuration.

@Bean
    public javax.validation.Validator validator() {
        return new org.springframework.validation.beanvalidation.LocalValidatorFactoryBean();
    }

also check if your configuration has @EnableWebMvc annotation

Upvotes: 0

DaveyDaveDave
DaveyDaveDave

Reputation: 10562

@Funtik's suggestion about making it an @Controller is worth a try, but I suspect your problem is that you're not handling the error in your controller.

I'm not an expert on this, so recommend you do a little further reading, but I think you need to add a BindingResult, which will be populated in the event of validation errors. You then test that, and respond appropriately. So, I'd change your controller to something like:

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(@Valid UserDTO userDTO, BindingResult bindingResult, @Context UriInfo uriInfo) {
  if (bindingResult.hasErrors()) {
    return Response... // some error code, or however you want to handle validation errors
  }

  User user = UserParser.parse(userDTO);
  userService.save(user);
  final URI uri = uriInfo.getAbsolutePathBuilder().path(String.valueOf(user.getId())).build();
  return Response.created(uri).build();
}

It's probably not an issue right now, but if you add a @ModelAttribute annotated parameter to your method, bear in mind that the BindingResult parameter needs to appear immediately after it, otherwise things don't work. I can't remember the exact problem you see if you don't do it, but it's tricky to find if you don't know the above. I've definitely seen it documented in the Spring docs somewhere, but can't find anything other than this forum post right now.

Upvotes: -1

Related Questions