Search

Dark theme | Light theme
Showing posts with label Ratpacked:Handlers. Show all posts
Showing posts with label Ratpacked:Handlers. Show all posts

June 7, 2017

Ratpacked: Assert No Exceptions Are Thrown With RequestFixture

Writing unit tests for our handlers in Ratpack is easy with RequestFixture. We invoke the handle method and use a Handler or Chain we want to test as argument. We can provide extra details on the fixture instance with a second argument, for example adding objects to the registry or setting the request method. The handle method returns a HandlingResult object. This object has the method exception that we can use to see if an exception occurred in our code under test. The method throws a HandlerExceptionNotThrownException if the expected exception doesn't occurr.

In the following example we have two feature methods to check if an exception occurred or not:

package sample

import ratpack.handling.Context
import ratpack.handling.Handler
import ratpack.test.handling.RequestFixture
import spock.lang.Specification

class HandlerSpec extends Specification {

    def 'check exception is thrown'() {
        given:
        def result = RequestFixture.handle new SampleHandler(true), Action.noop()

        expect:
        result.exception(Exception).message == 'Sample exception'
    }

    def 'check no exception is thrown'() {
        given:
        def result = RequestFixture.handle new SampleHandler(false), Action.noop()
        
        when:
        result.exception(Exception)

        then:
        thrown(HandlerExceptionNotThrownException)
    }
    
}

class SampleHandler implements Handler {
    
    /**
     * Indicate if we need to create an 
     * error with an exception or not.
     */
    private final boolean throwException = false

    SampleHandler(final boolean throwException) {
        this.throwException = throwException
    }

    @Override
    void handle(final Context ctx) throws Exception {
        if (throwException) {
            // Throw a sample exception.
            ctx.error(new Exception('Sample exception'))
            ctx.response.send()
        } else {
            // No exceptions.
            ctx.response.send('OK')
        }
    }
    
}

Instead of using the exception method of HandlingResult we can add a custom ServerErrorHandler to the fixture registry. Exceptions are handled by the error handler and we can check if an exception occurred or not via the error handler. In the following code we use a custom error handler:

package sample

import ratpack.error.ServerErrorHandler
import ratpack.handling.Context
import ratpack.handling.Handler
import ratpack.test.handling.RequestFixture
import spock.lang.Specification

class HandlerSpec extends Specification {

    /**
     * Error handler to capture exceptions.
     */
    private specErrorHandler = new SpecErrorHandler()

    /**
     * Add error handler as {@link ServerErrorHandler}
     * implementation to the fixture registry.
     */
    private fixtureErrorHandler = { fixture ->
        fixture.registry.add ServerErrorHandler, specErrorHandler
    }
    
    def 'check exception is thrown'() {
        when:
        RequestFixture.handle new SampleHandler(true), fixtureErrorHandler

        then:
        specErrorHandler.exceptionThrown()
        
        and:
        specErrorHandler.throwable.message == 'Sample exception'
    }

    def 'check no exception is thrown'() {
        when:
        RequestFixture.handle new SampleHandler(false), fixtureErrorHandler

        then:
        specErrorHandler.noExceptionThrown()
    }
    
}

class SampleHandler implements Handler {
    
    /**
     * Indicate if we need to create an 
     * error with an exception or not.
     */
    private final boolean throwException = false

    SampleHandler(final boolean throwException) {
        this.throwException = throwException
    }

    @Override
    void handle(final Context ctx) throws Exception {
        if (throwException) {
            // Throw a sample exception.
            ctx.error(new Exception('Sample exception'))
            ctx.response.send()
        } else {
            // No exceptions.
            ctx.response.send('OK')
        }
    }
    
}

/**
 * Simple implementation for {@link ServerErrorHandler}
 * where we simply store the original exception and 
 * add utility methods to determine if an exception is
 * thrown or not.
 */
class SpecErrorHandler implements ServerErrorHandler {
    
    /**
     * Store original exception.
     */
    private Throwable throwable

    /**
     * Store exception in {@link #throwable} and 
     * set response status to {@code 500}.
     * 
     * @param context Context for request.
     * @param throwable Exception thrown in code.
     * @throws Exception Something goes wrong.
     */
    @Override
    void error(final Context context, final Throwable throwable) throws Exception {
        this.throwable = throwable
        context.response.status(500)
    }

    /**
     * @return {@code true} if error handler is invoked, {@code false} otherwise.
     */
    boolean exceptionThrown() {
        throwable != null
    }

    /**
     * @return {@code true} if error handler is not invoked, {@code false} otherwise.
     */
    boolean noExceptionThrown() {
        !exceptionThrown()
    }
    
}

Written with Ratpack 1.4.5.

January 7, 2016

Ratpacked: Add Chains Via Registry

In a previous blog post we learned how to use HandlerDecorator.prepend to add common handlers via the registry in our application. The type of handlers suitable for this approach were handlers that had common functionality for the rest of the handlers. If we want to add a Chain implementation, containing handlers and maybe even path information, we cannot use the prepend method, must write our own implementation of the HandlerDecorator interface. This can be useful when we want to re-use a Chain in multiple applications. We write a module that adds the Chain implementation to the registry and we don't have to write any code in the handlers section for the Chain to work. This blog post is inspired by a conversation on the Ratpack Slack channel recently.

First we create a simple handler that renders a result:

// File: src/main/groovy/com/mrhaki/ratpack/Ping.groovy
package com.mrhaki.ratpack

import ratpack.groovy.handling.GroovyChainAction

/**
 * Implementation of a {@link ratpack.handling.Chain} interface
 * by extending {@link GroovyChainAction}, so
 * we can use Groovy DSL support in the
 * {@link Ping#execute} method.
 */
class Ping extends GroovyChainAction {
    
    @Override
    void execute() throws Exception {
        // What we normally would write
        // in the handlers{} section
        // of Ratpack.groovy.
        path('ping') {
            render 'Ratpack rules!'
        }
    }
}

First we register an instance of the Ping class using the multiBindInstance method in the bindings section of our Ratpack.groovy file:

import com.mrhaki.ratpack.Ping
import ratpack.handling.HandlerDecorator
import ratpack.handling.Handlers
import ratpack.registry.Registry

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // We use multiBindInstance so multiple
        // implementations of HandlerDecorator
        // can be added to the registry.
        // The HandlerDecorator is implemented with
        // a closure. We add the Ping chain after 
        // other handlers (if set).
        multiBindInstance(
                HandlerDecorator, { Registry registry, Handler rest ->
                    Handlers.chain(rest, Handlers.chain(registry, new Ping()))
                } as HandlerDecorator)

    }
    
    handlers {
        get {
            render 'Ratpack is awesome!'
        } 
    }

}

If we start the application we get results when we invoke http://localhost:5050/ and http://localhost:5050/ping:

$ http -b localhost:5050/
Ratpack is awesome!

$ http -b localhost:5050/ping
Ratpack rules!

$

Instead of using the multiBindInstance method in the bindings section we can create a module and register our Ping class in the module:

// File: src/main/groovy/com/mrhaki/ratpack/PingModule.groovy
package com.mrhaki.ratpack

import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import ratpack.handling.Handler
import ratpack.handling.HandlerDecorator
import ratpack.handling.Handlers
import ratpack.registry.Registry

/**
 * Module to register the {@link Ping}
 * chain with the registry.
 */
class PingModule extends AbstractModule {
    
    @Override
    protected void configure() {
        // We need the Multibinder, because other modules
        // can also register HandlerDecorator implementations.
        Multibinder
                .newSetBinder(binder(), HandlerDecorator)
                .addBinding()
                .toInstance({ Registry registry, Handler rest ->
                    Handlers.chain(rest, Handlers.chain(registry, new Ping())
                } as HandlerDecorator)
    }
    
}

The Ratpack.groovy file now is as follows:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.Ping
import com.mrhaki.ratpack.PingModule

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        module PingModule
    }
    
    handlers {
        get {
            render 'Ratpack is awesome!'
        } 
    }

}

To make the module more reusable we want to make the path binding for the Ping handler configurable. So users can include the module, but still can set the path binding for their application. Things get a bit more complicated now and we need some changes in our code. First we write a class with the configuration data for the module. We only have one property path that is used for the binding.

// File: src/main/groovy/com/mrhaki/ratpack/PingModuleConfig.groovy
package com.mrhaki.ratpack

class PingModuleConfig {
    String path
}

Our Ping class now accepts an instance of the configuration to get the path value:

// File: src/main/groovy/com/mrhaki/ratpack/Ping.groovy
package com.mrhaki.ratpack

import ratpack.groovy.handling.GroovyChainAction

import javax.inject.Inject

/**
 * Implementation of a {@link ratpack.handling.Chain} interface
 * by extending {@link GroovyChainAction}, so
 * we can use Groovy DSL support in the
 * {@link Ping#execute} method.
 */
class Ping extends GroovyChainAction {
    
    String pingPathBinding = 'ping'
    
    @Inject
    Ping(final PingModuleConfig config) {
        pingPathBinding = config.path
    }
    
    @Override
    void execute() throws Exception {
        path(pingPathBinding) {
            render 'Ratpack rules!'
        }
    }
}

Now we change the module and make it a ConfigurableModule. Notice we bind the Ping class so the dependency injection of our configuration works. Then we use a Provider to get the fully configured instance of the Ping class.

// File: src/main/groovy/com/mrhaki/ratpack/PingModule.groovy
package com.mrhaki.ratpack

import com.google.inject.Provider
import com.google.inject.multibindings.Multibinder
import ratpack.guice.ConfigurableModule
import ratpack.handling.Handler
import ratpack.handling.HandlerDecorator
import ratpack.handling.Handlers
import ratpack.registry.Registry

class PingModule extends ConfigurableModule<PingModuleConfig> {

    @Override
    protected void configure() {
        bind(Ping)

        final Provider<Ping> pingProvider = getProvider(Ping)

        Multibinder
                .newSetBinder(binder(), HandlerDecorator)
                .addBinding()
                .toProvider({ -> 
                    { Registry registry, Handler rest ->
                        Handlers.chain(rest, Handlers.chain(registry, pingProvider.get()))
                    } as HandlerDecorator
                } as Provider<HandlerDecorator>)
    }

}

Finally we change our Ratpack application and configure the path to be pong:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.PingModule
import com.mrhaki.ratpack.PingModuleConfig
import ratpack.config.ConfigData

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        final ConfigData configData = ConfigData.of { builder ->
            builder.props(['ping.path': 'pong']).build()
        }
        moduleConfig(PingModule, configData.get('/ping', PingModuleConfig))
    }
    
    handlers {
        get {
            render 'Ratpack is awesome!'
        }
    }

}

We can make our requests and look at the results:

$ http -b localhost:5050/
Ratpack is awesome!

$ http -b localhost:5050/pong
Ratpack rules!

$ http -b localhost:5050/ping
Client error 404

$

@guspower mentioned on the Ratpack Slack channel that we can also implement the HandlerDecorator interface directly. This cleans up our module code. Here is the implementation:

// File: src/main/groovy/com/mrhaki/ratpack/PingHandlerDecorator.groovy
package com.mrhaki.ratpack

import com.google.inject.Provider
import ratpack.handling.Handler
import ratpack.handling.HandlerDecorator
import ratpack.handling.Handlers
import ratpack.registry.Registry

class PingHandlerDecorator implements HandlerDecorator {

    /**
     * Provider for Ping is automatically injected
     * via the constructor.
     */
    private final Provider<Ping> pingProvider

    @Inject
    PingHandlerDecorator(Provider<Ping> pingProvider) {
        this.pingProvider = pingProvider
    }

    @Override
    Handler decorate(Registry registry, Handler rest) throws Exception {
        // Here is the code we had in the module before.
        Handlers.chain rest, Handlers.chain(registry, pingProvider.get())
    }

}

We now change the module class, which is even simpler now:

// File: src/main/groovy/com/mrhaki/ratpack/PingModule.groovy
package com.mrhaki.ratpack

import com.google.inject.Provider
import com.google.inject.multibindings.Multibinder
import ratpack.guice.ConfigurableModule
import ratpack.handling.Handler
import ratpack.handling.HandlerDecorator
import ratpack.handling.Handlers
import ratpack.registry.Registry

class PingModule extends ConfigurableModule<PingModuleConfig> {

    @Override
    protected void configure() {
        bind(Ping)
        bind(PingHandlerDecorator)
    }

}

Written with Ratpack 1.1.1.

Ratpacked: Add Common Handlers Via The Registry

In our Ratpack application we can have handlers that need to be invoked for every request. For example the handler needs to set a response header and will use the Context.next() method to continue with the rest of the handlers. When we have such a handler we can use the all method of a Chain instance. This happens in the handlers section of our application definition. We can also register such handlers directly in the registry. We can even use a Module to register the handler implementation. To register a handler in the registry we must use the HandlerDecorator interface. The interface has a method prepend that will encapsulate a Handler implementation and makes it available before any other handlers.

In the following sample we have a Handler implementation that checks if the RequestId object is available. If so then the value is set as a response header:

// File: src/main/groovy/com/mrhaki/ratpack/RequestIdHeader.groovy
package com.mrhaki.ratpack

import ratpack.handling.Context
import ratpack.handling.Handler
import ratpack.handling.RequestId

/**
 * Handler that adds the value of
 * the {@link RequestId} instance, if
 * available, to the response header.
 */
class RequestIdHeader implements Handler {
    
    private static final String HEADER_KEY = 
            'X-Ratpack-Request-Identifier'

    @Override
    void handle(final Context context) throws Exception {
        // Optinally get RequestId from the registry.
        final Optional<String> requestId = 
                context.maybeGet(RequestId)
        
        if (requestId.present) {
            // If RequestId is set then add it to the 
            // response header.
            context.response.headers.set(
                    HEADER_KEY, 
                    requestId.get())
        }
        
        // Continue with the rest of the handlers.
        context.next()
    }
    
}

In our Ratpack application uses the HandlerDecorator.prepend method to register our handler in the bindings section:

import com.mrhaki.ratpack.RequestIdHeader
import ratpack.handling.HandlerDecorator

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // We use multiBindInstance so multiple
        // implementations of HandlerDecorator
        // can be added to the registry.
        multiBindInstance( 
            HandlerDecorator, 
            HandlerDecorator.prepend(new RequestIdHeader()))
    }

    handlers {
        get {
            render 'Ratpack is awesome'
        }
    }
}

Alternatively we can create a module:

// File: src/main/groovy/com/mrhaki/RequestIdModule.groovy
package com.mrhaki.ratpack

import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import ratpack.handling.HandlerDecorator

/**
 * Module to register the {@link RequestIdHeader}
 * handler with the registry.
 */
class RequestIdModule extends AbstractModule {
    
    @Override
    protected void configure() {
        // We need the Multibinder, because other modules
        // can also register HandlerDecorator implementations.
        Multibinder
                .newSetBinder(binder(), HandlerDecorator)
                .addBinding()
                .toInstance(
                    HandlerDecorator.prepend(new RequestIdHeader()))
    }
    
}

We change our Ratpack application to use the module:

import com.mrhaki.ratpack.RequestIdHeader
import com.mrhaki.ratpack.RequestIdModule
import ratpack.handling.HandlerDecorator

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // Register module.
        module RequestIdModule
    }

    handlers {
        get {
            render 'Ratpack is awesome'
        }
    }
}

Written with Ratpack 1.1.1.

December 31, 2015

Ratpacked: Execute Handlers Based On Accept Header

A client of our Ratpack application can send a HTTP Accept header to indicate the type of response the client expects or can handle. We can restrict our application to certain types with the accepts method of the Handlers class. We can define one or more String values that denote the types that a client can use and our application responds to. If the value for the Accept header is one of the given values then the next handlers are invoked otherwise a client error 406 Not Acceptable is returned.

In the following example Ratpack application definition we use the accepts method so only values of application/json and application/xml:

// File: src/ratpack/ratpack.groovy
import static ratpack.handling.Handlers.accepts
import static ratpack.http.MediaType.APPLICATION_JSON
import static ratpack.groovy.Groovy.ratpack


ratpack {
    handlers {
        // If the Accept header contains application/json or
        // application/xml then continue with the rest of the
        // handlers.
        all(accepts(APPLICATION_JSON, 'application/xml'))

        get {
            render 'Developing with Ratpack is fun.'
        }
    }
}

Written with Ratpack 1.1.1.

Ratpacked: Implicit Registry Retrieval With InjectionHandler

If we write our own handler class it is not so difficult to get objects from the registry and use them in the code. In our handler we have a handle method and we get a Context object as argument. From the Context we get access to objects in the registry. Instead of writing code to get the objects from the registry we can use the InjectionHandler as superclass for our handler. The InjectionHandler class has a handle method that will look for a handle method in our implementation class with a first argument of type Context. Then at least one other argument must be defined in the method signature. The types of the other arguments are used to get the corresponding objects from the registry. We don't write the code to get the object from the registry ourselves, but rely on the implementation in the InjectionHandler class.

The following example handler class extends from the InjectionHandler. We write a handle method and from the signature we see that we have a dependency on the type Messages. In the implementation of the handle method we can rely on the InjectionHandler class to get an object from the registry with type Messages.

package com.mrhaki.ratpack

import ratpack.handling.Context
import ratpack.handling.InjectionHandler

/**
 * Extend from InjectionHandler. The InjectionHandler
 * has a handle method implementation that will 
 * look for a handle method in this implementing class
 * and uses the argument types to get object from the
 * registry.
 */
class MessagesHandler extends InjectionHandler {

    /**
     * New (not overriden) handle method where 
     * the first argument must be a Ratpack Context. 
     * The next arguments are of types
     * that are available on the registry. 
     * 
     * @param context Ratpack context to interact with.
     * @param messages Messages to return a message.
     */
    void handle(final Context context, final Messages messages) {
        // An instance of Messages is already available
        // thanks to the InjectionHandler superclass.
        context.render(messages.message)
    }
    
}

Written with Ratpack 1.1.1.

December 23, 2015

Ratpacked: Respond To Custom MIME Types

In Ratpack we can use the byContent method on the Context object to send different responses based on the requested MIME type from the client. There is already support for application/json, application/xml, text/plain and text/html MIME types with corresponding methods of the ByContentSpec object that is passed as argument to the byContent method. We can match on a custom MIME type with the method type and specify the MIME type. If the type matches we can create a response.

In the following example application we have a custom renderer for a User object:

// File: src/main/groovy/com/mrhaki/ratpack/UserRenderer.groovy
package com.mrhaki.ratpack

import ratpack.handling.ByContentSpec
import ratpack.handling.Context
import ratpack.jackson.Jackson
import ratpack.render.RendererSupport

import static ratpack.groovy.Groovy.markupBuilder

class UserRenderer extends RendererSupport<User> {
    
    @Override
    void render(Context context, User user) throws Exception {
        context.byContent { ByContentSpec spec ->
            spec
                .type('application/vnd.com.mrhaki.user+json;v=1') {
                    context.render(Jackson.json(user))
                }
                .type('application/vnd.com.mrhaki.user+xml;v=1') {
                    context.render(markupBuilder('application/xml', 'UTF-8') {
                        delegate.user {
                            username(user.username)
                        }
                    })
                }
        }
    }
    
}

Next we use it in our Ratpack definition:

import com.mrhaki.ratpack.User
import com.mrhaki.ratpack.UserRenderer

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // Register renderer for User objects.
        bind(UserRenderer)
        
        // Create two sample users.
        bindInstance(['mrhaki', 'hubert'].collect { String name ->
            new User(username: name)
        })        
    }

    handlers {
        get('user/:username') { List<User> users ->
            // Get value for username token.
            final String username = pathTokens.username
            
            // Find user in list of users.
            final User user = users.find { User user ->
                user.username == username
            }
            
            // Render user object.
            render(user)
        }
    }
}

When we make a request with different accept MIME types we see different results:

$ http localhost:5050/user/mrhaki Accept:application/vnd.com.mrhaki.user+xml;v=1
HTTP/1.1 200 OK
connection: keep-alive
content-encoding: gzip
content-type: application/xml
transfer-encoding: chunked

<user>
    <username>mrhaki</username>
</user>

$ http localhost:5050/user/mrhaki Accept:application/vnd.com.mrhaki.user+json;v=1
HTTP/1.1 200 OK
connection: keep-alive
content-encoding: gzip
content-type: application/vnd.com.mrhaki.user+json;v=1
transfer-encoding: chunked

{
    "username": "mrhaki"
}

$

Written with Ratpack 1.1.1.

Ratpacked: Add Response Time To Response Header

Ratpack has the class ratpack.handling.ReponseTimer which adds a header with the name X-Response-Time to the response. The value is the time spent in code from when the request comes in and the response is sent out. ResponseTimer is a handler we can add in our application. Alternatively we can use the static method decorator to get a handler decorator. With a handler decorator we can use the registry to add handler logic in our application.

First we use the ResponseTimer as a handler:

@Grab("io.ratpack:ratpack-groovy:1.1.1") 
import ratpack.handling.ResponseTimer

import static ratpack.groovy.Groovy.ratpack 

ratpack { 
    handlers { 
        // Add ResponseTimer for 
        // all requests. 
        all(new ResponseTimer())

        get { 
            render "Ratpack rocks!" 
        } 
    } 
} 

We get the same result if we add the handler decorator via the registry:

@Grab("io.ratpack:ratpack-groovy:1.1.1") 
import ratpack.handling.ResponseTimer

import static ratpack.groovy.Groovy.ratpack 

ratpack { 
    bindings {
        // Add ResponseTimer for 
        // all requests. 
        bindInstance(ResponseTimer.decorator())
    }
    handlers { 
        get { 
            render "Ratpack rocks!" 
        } 
    } 
} 

When we make a request and look at the response header we see a header with the name X-Response-Time:

$ http localhost:5050
HTTP/1.1 200 OK
X-Response-Time: 1.00000
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

Ratpack rocks!

$

Written with Ratpack 1.1.1.

December 22, 2015

Ratpacked: Using Names With Regular Expression Tokens

In a previous post we have seen how to use regular expressions for path tokens. We can also name the path token for which the regular expression applies. This makes it easier to get the value in our handler code: we can just refer to the name. Also we can add a question mark to the name to make the token optional.

In the following example we use the name conferenceName for the path token with the regular expression Gr\w+:

import ratpack.groovy.template.MarkupTemplateModule
import ratpack.handling.Context
import ratpack.path.PathBinder
import ratpack.path.PathBinding

import static ratpack.groovy.Groovy.groovyMarkupTemplate
import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {

        prefix('conferences') {
            // Here we define a path with the named 
            // token conferenceName.
            path(/:conferenceName:Gr\w+/) { 
                // The token has a name and a regular expression.
                // If the value matches the regular expression,
                // we can refer to the value using the name.
                final String conferenceName = pathTokens.conferenceName
                
                // Use matched value in response.
                render "Conference($conferenceName)"
            }
        }

    }
}

Written with Ratpack 1.1.1.