Suppose we want to support partial JSON responses in our Ratpack application. The user must send a request parameter with a list of fields that need to be part of the response. In our code we must use the value of the request parameter and output only the given properties of an object. We implement this logic using a custom renderer in Ratpack. Inside the renderer we can get access to the request parameters of the original request.
In our example Ratpack application we have a Course
class, which is a simple class withs some properties:
// File: src/main/groovy/mrhaki/ratpack/Course.groovy package mrhaki.ratpack import groovy.transform.Immutable @Immutable class Course { String name String teacher Integer maxOccupation }
Next we create a custom renderer for our Course
class. We extend the RendererSupport
class and override the render
method:
// File: src/main/groovy/mrhaki/ratpack/CourseRenderer.groovy package mrhaki.ratpack import ratpack.handling.Context import ratpack.render.RendererSupport import static ratpack.jackson.Jackson.json class CourseRenderer extends RendererSupport<Course> { @Override void render(final Context context, final Course course) throws Exception { // Get request parameter fields with a comma separated list // of field names to include in the output. final String paramFields = context.request.queryParams.get('fields') if (paramFields) { // Transform comma separated property names to a Set. final Set<String> coursePropertyNames = paramFields.tokenize(',').toSet() // Create Map with only Course properties that need to // be included. final Map partialCourse = filterProperties(course, coursePropertyNames) // Render Map. context.render(json(partialCourse)) } else { // No fields request parameter so we can return // the original Course object. context.render(json(course)) } } /** * Find all properties in the object that are in the collection * of property names. * * @param object Object with properties to filter * @param propertyNames Names of properties to find * @return Map with properties */ private Map filterProperties( final Object object, final Set<String> propertyNames) { object.properties.findAll { property -> property.key in propertyNames } } }
Finally we need to add the CourseRenderer
to the Ratpack registry. Ratpack will find the renderer when we want to render a Course
object. This happens automatically, we don't have to do anything ourselves. The following Ratpack application configuration adds our CourseRenderer
with the bind
method. We also add a endpoint to show the contents of a sample Course
object.
// File: src/ratpack/ratpack.groovy import mrhaki.ratpack.Course import mrhaki.ratpack.CourseRenderer import ratpack.registry.Registry import static ratpack.groovy.Groovy.ratpack ratpack { bindings { // Add to registry, so Ratpack can use // it to render a Course object. bind CourseRenderer } handlers { all { final Course course = new Course( name: 'Ratpack rules 101', teacher: 'mrhaki', maxOccupation: 450) next(Registry.single(course)) } get('course') { Course course -> render(course) } } }
Let's try several requests using the fields
request parameter and without the fields
request parameter using HTTPie as client:
$ http -b http://localhost:5050/course { "maxOccupation": 450, "name": "Ratpack rules 101", "teacher": "mrhaki" } $ http -b http://localhost:5050/course fields==name,teacher { "name": "Ratpack rules 101", "teacher": "mrhaki" }
Written with Ratpack 1.3.3.