Ratpack is a lean library to build HTTP applications. Ratpack for example doesn't include functionality to validate forms that are submitted from a web page. To add form validation to our Ratpack application we must write our own implementation.
Let's write a simple application with a HTML form. We will use Hibernate Validator as a JSR 303 Bean Validation API implementation to validate the form fields. IN our application we also use the MarkupTemplateModule
so we can use Groovy templates to generate HTML. We have a simple form with two fields: username and email. The username field is required and the email field needs to have a valid e-mail address. The following class uses annotations from Hibernate Validator to specify the constraints for these two fields:
// File: src/main/groovy/com/mrhaki/ratpack/validation/User.groovy package com.mrhaki.ratpack.validation import groovy.transform.CompileStatic import groovy.transform.ToString import org.hibernate.validator.constraints.Email import org.hibernate.validator.constraints.NotEmpty import javax.validation.constraints.NotNull @CompileStatic @ToString(includeNames = true) class User implements Serializable { @NotEmpty String username @Email String email }
Now we write our Ratpack application to handle a form submitted to /submit
. To validate our User
class we must get a java.validation.Validator
instance. We use the buildDefaultValidatorFactory
method of the javax.validation.Validation
class to create a Validator
instance. We then add it to the registry, so handlers can use it when needed.
Next we use the parse
method of Context
to parse the input to a Form
instance. We then use the values from the Form
instance to populate our properties in the User
class. We validate the User
instance and get back a set of ConstraintViolation
objects if there any validation errors. In our logic we display the validation errors on the HTML page, if there are no errors a next page is shown.
// File: src/ratpack/Ratpack.groovy import com.mrhaki.ratpack.validation.User import ratpack.form.Form import ratpack.groovy.template.MarkupTemplateModule import javax.validation.ConstraintViolation import javax.validation.Validation import javax.validation.Validator import static ratpack.groovy.Groovy.groovyMarkupTemplate import static ratpack.groovy.Groovy.ratpack ratpack { bindings { // Add module to support Groovy templates. module(MarkupTemplateModule) // Create Validator instance to be used // in the handlers. bindInstance(Validator, Validation.buildDefaultValidatorFactory().validator) } handlers { // Handle submitted data. The closure // argument has the Validator instance // we created in the bindings{} block. post('submit') { Validator validator -> // Parse the values that are submitted to // a Form object. parse(Form) .map { Form form -> // Transform Form to a User object // and set the username and email // properties. new User( username: form.username, email: form.email) } .then { User user -> // Validate the User object. final Set<ConstraintViolation<User>> constraintViolations = validator.validate(user) if (constraintViolations.size() > 0) { // If there are violations we convert them // to a map where the keys are the field names // and the value the ConstraintViolation object. // We use this in our template to display the errors. final Map<String, ConstraintViolation<User>> formErrors = constraintViolations.collectEntries { violation -> [(violation.propertyPath.toString()): violation] } // Render page so we can display errors. render groovyMarkupTemplate('index.gtpl', user: user, formErrors: formErrors) } else { // Everything was ok, redirect to // different page. redirect("thankyou?username=${user.username}") } } } get('thankyou') { // Show page with the value of request parameter username. final String username = request.queryParams.username render groovyMarkupTemplate('thankyou.gtpl', username: username) } get { // Show default page. render groovyMarkupTemplate('index.gtpl', user: new User()) } files { dir "public" } } }
With the following two Groovy templates we can render the HTML we want:
// File: src/ratpack/template/index.gtpl yieldUnescaped '<!DOCTYPE html>' html { head { meta(charset:'utf-8') title("Ratpack: Form validation") link(href: '/images/favicon.ico', rel: 'shortcut icon') } body { section { h2 'Form' form(action: 'submit', method: 'POST') { def inputFields = [ username: [type: 'text', value: user.username], email: [type: 'email', value: user.email] ] inputFields.each { fieldName, fieldConf -> div(style: formErrors?.get(fieldName) ? 'color: red' : '') { label(for: fieldName, "${fieldName.capitalize()}: ") input( type: fieldConf.type, value: fieldConf.value, name: fieldName, id: fieldName) if (formErrors?.get(fieldName)) { yield(formErrors[fieldName].message) } } } div { input(type: 'submit', value: 'Submit') } } } } }
// File: src/ratpack/template/thankyou.gtpl yieldUnescaped '<!DOCTYPE html>' html { head { meta(charset:'utf-8') title("Ratpack: Thank you") link(href: '/images/favicon.ico', rel: 'shortcut icon') } body { section { h2 'Thank you' p "You have used our Ratpack application to register as ${username}." } } }
What is left is to add the dependencies for Hibernate Validator to our project.
// File: build.gradle plugins { id 'io.ratpack.ratpack-groovy' version '1.1.1' id 'com.github.johnrengelman.shadow' version '1.2.2' } repositories { jcenter() } dependencies { compile 'org.hibernate:hibernate-validator:5.2.2.Final' runtime 'javax.el:javax.el-api:2.2.4' runtime 'org.glassfish.web:javax.el:2.2.4' }
We are ready to run our application. Once we have started the application we can open the default page in our browser. If we leave the username empty and use a invalid email value we get the following response when we submit the form:
Written with Ratpack 1.1.1.