James Williams
LinkedInMastodonGithub

Serving Images (Favicons) with Ratpack Middleware

One of the things I noticed from running the sample material was that I would get a 404 error when clients try to retrieve the favicon.ico file. I thought it would be a matter of just dropping the favicon.ico file into the app directory. If only it were that simple... What ended up working is creating a new route for the favicon and working some HttpResponse mojo to get Ratpack to serve up the image. Adding a route directly to our app file is nice but a bit messy. I split out the new route into its own file in an attempt to create a *pseudo-Ratpack middleware*. The file is listed below:

class Helpers {
    static addFavicon = { app ->
    def handler = {
        try {
        response.setContentType("image/ico");
        file = new File('favicon.ico')
        response.setContentLength((int)file.length())

        inStream = new FileInputStream(file)
        out = response.getOutputStream();

        // Copy the contents of the file to the output stream
        byte[] buf = new byte[1024];
        int count = 0;
        while ((count = inStream.read(buf)) >= 0) {
            out.write(buf, 0, count);
        }
        inStream.close();
        out.close();
        } catch(Exception ex) {}
        ""
    }

    app.get('/favicon.ico', handler)
    }
}

The empty string at the end of the handler is to make sure Ratpack doesn't balk at us handling the strream ourselves.

The code from the last post was just a closure so we need to modify it a little bit to automagically handle the favicons. Be aware that saving the file as a script in this manner(below) somewhat breaks the script from working with the ratpack script file. The reason for this is because the ratpack file expects there to only be a closure in any file you pass to it. The scaffolding we added to the file duplicates what the ratpack script was already doing so it fusses at you ... something about being DRY ;) ... but you've already deployed the libs to your .groovy so you can run it like any other groovy script making sure to pass in the Helpers.groovy file as well.

import com.bleedingwolf.ratpack.*
import groovy.ratpack.*

def app = Ratpack.app({
    get("/") {
    response.sendRedirect("/helloworld")
    }
    get("/helloworld") {
    "Hello, World!"
    }   
    get("/error/:error") {
    response.sendError(new Integer(urlparams.error))
    }   
    get("/data") {
    request.toString()
    }   
})

Helpers.addFavicon(app)
RatpackServlet.serve(app)

Alternatively, we can add the Helpers.... line to the ratpack script file. Or even yet, one might provide a gradle script and have the file be copied somewhere that is accessible to all the Ratpack apps.