Fork me on GitHub

Filtering

Introduction

Powerful powerful powerful. This is what filters are.

Let's say you want to implement authorization in your application. Only users authorized should be able to access a certain route and controller. Otherwise they should get an error page.

There are two principal ways how to add filters to your controller:

Option 1 is to add filters in the Routes file:

router.GET().route("/index").filters(SecureFilter.class).with(AppController::index);  

Option 2 is to annotate our controller method with a @FilterWith annotation:

@FilterWith(SecureFilter.class)  // Only let authorized users execute the controller method
public Result secureIndex() {    
    /// do something
}    

Ninja will execute the filter before the method secureIndex(…)will be called.

The SecureFilter.class itself looks like:

public class SecureFilter implements Filter {

    /** If a username is saved we assume the session is valid */
    public final String USERNAME = "username";

    @Override
    public Result filter(FilterChain chain, Context context) {

        // if we got no cookies we break:
        if (context.getSession() == null
                || context.getSession().get(USERNAME) == null) {

            return Results.forbidden().html().template("/views/forbidden403.ftl.html");

        } else {
            return chain.next(context);
        }

    }
}

SecureFilter looks into the session and tries to get a variable called “username”. If it can do so we can assume the user has been authenticated by us. Please refer to the sessions section for more information why that is the case). The filter then simply calls the next filer in the chain.

return chain.next(context);

However - if the variable username is not there we will immediately break the filter chain and will never call the method itself. It instead will return a forbidden status code and render a forbidden view.

return Results.forbidden().html().template("/views/forbidden403.ftl.html");

Chaining filters

You can define filters that run sequentially either in the Routes file …

router.GET().route("/index").filters(LoggerFilter.class, TeaPotFilter.class).with(AppController::index);  

… or by specifying them as annotation:

@FilterWith({
    LoggerFilter.class, 
    TeaPotFilter.class})
public Result teapot(Context context) {
    // do something
}    

This method will first call the LoggerFilter and then the TeaPotFilter. Each of the individual filters can break the chain or even alter the result and the context.

@FilterWith - class level and inheritance

You can put @FilterWith annotations on method level, but also on class level. If you use @FilterWith on class level all methods of this class will be filtered.

Sometimes it is also useful to have a BaseController that is annotated with @FilterWith. Other controllers extending BaseContoller will automatically inherit @FilterWith.

Global filters

Sometimes it's needed to run filters for every request reaching your application.

This is possible by creating a class Filters in package conf that implements the interface ApplicationFilters:

package conf;

import java.util.List;
import ninja.application.ApplicationFilters;
import ninja.Filter;

public class Filters implements ApplicationFilters {

    @Override
    public void addFilters(List<Class<? extends Filter>> filters) {
        filters.add(SecureFilter.class);
    }
}

You can override these global filters for individual routes in the Routes file:

router.GET().route("/index")
            .globalFilters(SecureFilter.class, LoggerFilter.class)
            .with(AppController::index);  

Passing values from a filter to other filters/controller

The most common filter examples for Ninja demonstrate how to use filters to set session values. However, non-session values can also be created in filters and passed onto other filters or your controller.

The context parameter included with the filter method supports setting arbitrary attributes (values) for the current request.

public class ExampleFilter implements Filter {

    @Override
    public Result filter(FilterChain chain, Context context) {

        context.setAttribute("foo", "bar");

        return chain.next(context);

    }
}

In another filter or your controller, you can fetch the value of “foo” by getting the attribute.

public MyController {

    @FilterWith(ExampleFilter.class)
    public Result exampleMethod(Context context) {
        
        String foo = context.getAttribute("foo");

        // foo will equal "bar"

    }
}

Alternatively, an even more powerful design pattern is to write your own ArgumentExtractor that extracts the attribute value and have it automatically injected into your controller. Visit the argument extractors page for more info.

BasicAuthFilter

Ninja ships with an implementation of HTTP Basic Authentication. You need to bind a UsernamePasswordValidator in your conf.Modules class and then annotate your secured controllers or controller methods with @FilterWith(BasicAuthFilter).

public class Module extends AbstractModule {

    @Override
    protected void configure() {

		// bind a UsernamePasswordValidator
		bind(UsernamePasswordValidator.class).toInstance(new UsernamePasswordValidator() {

			@Override
			public boolean validateCredentials(String username, String password) {
				return "user".equals(username) && "password".equals(password);
			}
		});
    }
}

public MyController {

    @FilterWith(BasicAuthFilter.class)
    public Result secureMethod(Context context) {
        // do something
    }
    
}