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 {

    @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 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 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.

Original blog post

shadow-left