6.14 Data Validation

It is easy to validate incoming data with Micronaut’s controllers with the Validation Advice.

First, add the Hibernate Validator configuration to your application:

  1. implementation("io.micronaut.beanvalidation:micronaut-hibernate-validator")
  1. <dependency>
  2. <groupId>io.micronaut.beanvalidation</groupId>
  3. <artifactId>micronaut-hibernate-validator</artifactId>
  4. </dependency>

We can validate parameters using javax.validation annotations and the Validated annotation at the class level.

Example

  1. import io.micronaut.context.annotation.Requires;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Controller;
  4. import io.micronaut.http.annotation.Get;
  5. import io.micronaut.validation.Validated;
  6. import javax.validation.constraints.NotBlank;
  7. import java.util.Collections;
  8. @Validated (1)
  9. @Controller("/email")
  10. public class EmailController {
  11. @Get("/send")
  12. public HttpResponse send(@NotBlank String recipient, (2)
  13. @NotBlank String subject) { (2)
  14. return HttpResponse.ok(Collections.singletonMap("msg", "OK"));
  15. }
  16. }

Example

  1. import io.micronaut.context.annotation.Requires;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Controller;
  4. import io.micronaut.http.annotation.Get;
  5. import io.micronaut.validation.Validated;
  6. import javax.validation.constraints.NotBlank;
  7. import java.util.Collections;
  8. @Validated (1)
  9. @Controller("/email")
  10. class EmailController {
  11. @Get("/send")
  12. HttpResponse send(@NotBlank String recipient, (2)
  13. @NotBlank String subject) { (2)
  14. HttpResponse.ok(Collections.singletonMap("msg", "OK"))
  15. }
  16. }

Example

  1. import io.micronaut.context.annotation.Requires
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Get
  5. import io.micronaut.validation.Validated
  6. import javax.validation.constraints.NotBlank
  7. import java.util.Collections
  8. @Validated (1)
  9. @Controller("/email")
  10. open class EmailController {
  11. @Get("/send")
  12. open fun send(@NotBlank recipient: String, (2)
  13. @NotBlank subject: String): HttpResponse<*> { (2)
  14. return HttpResponse.ok(Collections.singletonMap("msg", "OK"))
  15. }
  16. }
1Annotate controller with Validated
2subject and recipient cannot be blank.

If a validation error occurs a javax.validation.ConstraintViolationException will be thrown. By default, the integrated io.micronaut.validation.exception.ConstraintExceptionHandler is used to handle the exception leading to a behaviour as shown in the following test:

Example Test

  1. @Test
  2. public void testParametersAreValidated() {
  3. HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () ->
  4. client.toBlocking().exchange("/email/send?subject=Hi&recipient="));
  5. HttpResponse response = e.getResponse();
  6. assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
  7. response = client.toBlocking().exchange("/email/send?subject=Hi&recipient=me@micronaut.example");
  8. assertEquals(HttpStatus.OK, response.getStatus());
  9. }

Example Test

  1. def "test parameter validation"() {
  2. when:
  3. client.toBlocking().exchange('/email/send?subject=Hi&recipient=')
  4. then:
  5. def e = thrown(HttpClientResponseException)
  6. def response = e.response
  7. response.status == HttpStatus.BAD_REQUEST
  8. when:
  9. response = client.toBlocking().exchange('/email/send?subject=Hi&recipient=me@micronaut.example')
  10. then:
  11. response.status == HttpStatus.OK
  12. }

Example Test

  1. "test params are validated"() {
  2. val e = shouldThrow<HttpClientResponseException> {
  3. client.toBlocking().exchange<Any>("/email/send?subject=Hi&recipient=")
  4. }
  5. var response = e.response
  6. response.status shouldBe HttpStatus.BAD_REQUEST
  7. response = client.toBlocking().exchange<Any>("/email/send?subject=Hi&recipient=me@micronaut.example")
  8. response.status shouldBe HttpStatus.OK
  9. }

If you want your own ExceptionHandler to handle the constraint exceptions, you need to annotate it with @Replaces(ConstraintExceptionHandler.class)

Often, you may want to use POJOs as controller method parameters.

  1. /*
  2. * Copyright 2017-2020 original authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package io.micronaut.docs.datavalidation.pogo;
  17. import io.micronaut.core.annotation.Introspected;
  18. import javax.validation.constraints.NotBlank;
  19. @Introspected
  20. public class Email {
  21. @NotBlank (1)
  22. String subject;
  23. @NotBlank (1)
  24. String recipient;
  25. public String getSubject() {
  26. return subject;
  27. }
  28. public void setSubject(String subject) {
  29. this.subject = subject;
  30. }
  31. public String getRecipient() {
  32. return recipient;
  33. }
  34. public void setRecipient(String recipient) {
  35. this.recipient = recipient;
  36. }
  37. }
  1. /*
  2. * Copyright 2017-2020 original authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package io.micronaut.docs.datavalidation.pogo;
  17. import io.micronaut.core.annotation.Introspected;
  18. import javax.validation.constraints.NotBlank;
  19. @Introspected
  20. class Email {
  21. @NotBlank (1)
  22. String subject;
  23. @NotBlank (1)
  24. String recipient;
  25. }
  1. /*
  2. * Copyright 2017-2020 original authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package io.micronaut.docs.datavalidation.pogo
  17. import io.micronaut.core.annotation.Introspected
  18. import javax.validation.constraints.NotBlank
  19. @Introspected
  20. open class Email {
  21. @NotBlank (1)
  22. var subject: String? = null
  23. @NotBlank (1)
  24. var recipient: String? = null
  25. }
1You can use javax.validation annotations in your POJOs.

You need to annotate your controller with Validated. Also, you need to annotate the binding POJO with @Valid.

Example

  1. import io.micronaut.context.annotation.Requires;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Body;
  4. import io.micronaut.http.annotation.Controller;
  5. import io.micronaut.http.annotation.Post;
  6. import io.micronaut.validation.Validated;
  7. import javax.validation.Valid;
  8. import java.util.Collections;
  9. @Validated (1)
  10. @Controller("/email")
  11. public class EmailController {
  12. @Post("/send")
  13. public HttpResponse send(@Body @Valid Email email) { (2)
  14. return HttpResponse.ok(Collections.singletonMap("msg", "OK")); }
  15. }

Example

  1. import io.micronaut.context.annotation.Requires;
  2. import io.micronaut.http.HttpResponse;
  3. import io.micronaut.http.annotation.Body;
  4. import io.micronaut.http.annotation.Controller;
  5. import io.micronaut.http.annotation.Post;
  6. import io.micronaut.validation.Validated;
  7. import javax.validation.Valid;
  8. import java.util.Collections;
  9. @Validated (1)
  10. @Controller("/email")
  11. class EmailController {
  12. @Post("/send")
  13. HttpResponse send(@Body @Valid Email email) { (2)
  14. HttpResponse.ok(Collections.singletonMap("msg", "OK"))
  15. }
  16. }

Example

  1. import io.micronaut.context.annotation.Requires
  2. import io.micronaut.http.HttpResponse
  3. import io.micronaut.http.annotation.Body
  4. import io.micronaut.http.annotation.Controller
  5. import io.micronaut.http.annotation.Post
  6. import io.micronaut.validation.Validated
  7. import javax.validation.Valid
  8. import java.util.Collections
  9. @Validated (1)
  10. @Controller("/email")
  11. open class EmailController {
  12. @Post("/send")
  13. open fun send(@Body @Valid email: Email): HttpResponse<*> { (2)
  14. return HttpResponse.ok(Collections.singletonMap("msg", "OK"))
  15. }
  16. }
1Annotate controller with Validated
2Annotate the POJO which you wish to validate with @Valid

The validation of POJOs is shown in the following test:

  1. public void testPoJoValidation() {
  2. HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> {
  3. Email email = new Email();
  4. email.subject = "Hi";
  5. email.recipient = "";
  6. client.toBlocking().exchange(HttpRequest.POST("/email/send", email));
  7. });
  8. HttpResponse response = e.getResponse();
  9. assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
  10. Email email = new Email();
  11. email.subject = "Hi";
  12. email.recipient = "me@micronaut.example";
  13. response = client.toBlocking().exchange(HttpRequest.POST("/email/send", email));
  14. assertEquals(HttpStatus.OK, response.getStatus());
  15. }
  1. def "invoking /email/send parse parameters in a POJO and validates"() {
  2. when:
  3. Email email = new Email()
  4. email.subject = 'Hi'
  5. email.recipient = ''
  6. client.toBlocking().exchange(HttpRequest.POST('/email/send', email))
  7. then:
  8. def e = thrown(HttpClientResponseException)
  9. def response = e.response
  10. response.status == HttpStatus.BAD_REQUEST
  11. when:
  12. email = new Email()
  13. email.subject = 'Hi'
  14. email.recipient = 'me@micronaut.example'
  15. response = client.toBlocking().exchange(HttpRequest.POST('/email/send', email))
  16. then:
  17. response.status == HttpStatus.OK
  18. }
  1. "test poko validation"() {
  2. val e = shouldThrow<HttpClientResponseException> {
  3. val email = Email()
  4. email.subject = "Hi"
  5. email.recipient = ""
  6. client.toBlocking().exchange<Email, Any>(HttpRequest.POST("/email/send", email))
  7. }
  8. var response = e.response
  9. response.status shouldBe HttpStatus.BAD_REQUEST
  10. val email = Email()
  11. email.subject = "Hi"
  12. email.recipient = "me@micronaut.example"
  13. response = client.toBlocking().exchange<Email, Any>(HttpRequest.POST("/email/send", email))
  14. response.status shouldBe HttpStatus.OK
  15. }
Bean injection is supported in custom constraints with the hibernate validator configuration.