6.15 Data Validation

It is easy to validate incoming data with Micronaut controllers using 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.http.HttpResponse;
  2. import io.micronaut.http.annotation.Controller;
  3. import io.micronaut.http.annotation.Get;
  4. import io.micronaut.validation.Validated;
  5. import javax.validation.constraints.NotBlank;
  6. import java.util.Collections;
  7. @Validated (1)
  8. @Controller("/email")
  9. public class EmailController {
  10. @Get("/send")
  11. public HttpResponse send(@NotBlank String recipient, (2)
  12. @NotBlank String subject) { (2)
  13. return HttpResponse.ok(Collections.singletonMap("msg", "OK"));
  14. }
  15. }

Example

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

Example

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

If a validation error occurs a javax.validation.ConstraintViolationException is thrown. By default, the integrated io.micronaut.validation.exception.ConstraintExceptionHandler handles 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. }

To use your own ExceptionHandler to handle the constraint exceptions, annotate it with @Replaces(ConstraintExceptionHandler.class)

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

  1. import io.micronaut.core.annotation.Introspected;
  2. import javax.validation.constraints.NotBlank;
  3. @Introspected
  4. public class Email {
  5. @NotBlank (1)
  6. String subject;
  7. @NotBlank (1)
  8. String recipient;
  9. public String getSubject() {
  10. return subject;
  11. }
  12. public void setSubject(String subject) {
  13. this.subject = subject;
  14. }
  15. public String getRecipient() {
  16. return recipient;
  17. }
  18. public void setRecipient(String recipient) {
  19. this.recipient = recipient;
  20. }
  21. }
  1. import io.micronaut.core.annotation.Introspected
  2. import javax.validation.constraints.NotBlank
  3. @Introspected
  4. class Email {
  5. @NotBlank (1)
  6. String subject
  7. @NotBlank (1)
  8. String recipient
  9. }
  1. import io.micronaut.core.annotation.Introspected
  2. import javax.validation.constraints.NotBlank
  3. @Introspected
  4. open class Email {
  5. @NotBlank (1)
  6. var subject: String? = null
  7. @NotBlank (1)
  8. var recipient: String? = null
  9. }
1You can use javax.validation annotations in your POJOs.

Annotate your controller with Validated, and annotate the binding POJO with @Valid.

Example

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

Example

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.annotation.Body
  3. import io.micronaut.http.annotation.Controller
  4. import io.micronaut.http.annotation.Post
  5. import io.micronaut.validation.Validated
  6. import javax.validation.Valid
  7. @Validated (1)
  8. @Controller("/email")
  9. class EmailController {
  10. @Post("/send")
  11. HttpResponse send(@Body @Valid Email email) { (2)
  12. HttpResponse.ok(msg: "OK")
  13. }
  14. }

Example

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

Validation of POJOs is shown in the following test:

  1. @Test
  2. public void testPojoValidation() {
  3. HttpClientResponseException e = assertThrows(HttpClientResponseException.class, () -> {
  4. Email email = new Email();
  5. email.subject = "Hi";
  6. email.recipient = "";
  7. client.toBlocking().exchange(HttpRequest.POST("/email/send", email));
  8. });
  9. HttpResponse<?> response = e.getResponse();
  10. assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
  11. Email email = new Email();
  12. email.subject = "Hi";
  13. email.recipient = "me@micronaut.example";
  14. response = client.toBlocking().exchange(HttpRequest.POST("/email/send", email));
  15. assertEquals(HttpStatus.OK, response.getStatus());
  16. }
  1. def "invoking /email/send parse parameters in a POJO and validates"() {
  2. when:
  3. Email email = new Email(subject: 'Hi', recipient: '')
  4. client.toBlocking().exchange(HttpRequest.POST('/email/send', email))
  5. then:
  6. def e = thrown(HttpClientResponseException)
  7. def response = e.response
  8. response.status == HttpStatus.BAD_REQUEST
  9. when:
  10. email = new Email(subject: 'Hi', recipient: 'me@micronaut.example')
  11. response = client.toBlocking().exchange(HttpRequest.POST('/email/send', email))
  12. then:
  13. response.status == HttpStatus.OK
  14. }
  1. "test pojo 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.