Controllers

Controllers process requests for dynamic resources and orchestrate generation of an appropriate response. Controllers publish "action" methods which act as entry-points for RESTful request processing. You can inject request context into parameters of action methods. Controller invocation follows a defined lifecycle. Ways to use Dependency Injection with controllers.

Introduction

Controllers process requests for dynamic resources. The Resources chapter showed how Controller classes are mapped to dynamic resources.

Controllers are named after the C part in the MVC (Model-View-Controller) pattern. Their job is to accept a resource request and to orchestrate request processing. When a controller has finished, a response should have been generated. This usually means:

  1. Evaluate request parameters (normal parameters, path params, matrix-params, cookies, headers, request content)
  2. Call back-end services to fulfill the request, passing request parameters in appropriate form. The result produced by those servies is the model (the M in MVC). Civilian does not make any assumption about the form of your services and models.
  3. Turn the model into a view (the V in MVC), suitable to be sent as response. This might be a JSON or XML representation of the model, or a HTML page which displays model data, etc. Civilians content serialization and template system can help to implement this.
  4. Alternative responses would be to send an error status, or a redirect to another resource.
Controllers are not meant to contain a lot of code. Ideally they are just the glue between the frontend request and back-end services, translating request parameters, invoke back-end services and convert data models into response views.

What makes a controller a controller

A class in your application is recognized and treated as controller class if
  1. it directly or indirectly extends the Controller base class,
  2. its class name ends with Controller, e.g. SearchController or PrintController,
  3. it is placed in a package below the applications root controller package.
(As explained in the resources chapter, we scan the classpath to find all controller classes and build the resource tree. 2. and 3. help to do this efficiently. There are several ways to alter these requirements).

Controller action methods

For RESTful request processing, the controller should take request properties such as into account and create a specific response for the request.

Civilian helps to implement such a case-by-case analysis of requests with the concept of controller action methods:
Action methods are methods in a Controller class which act as entry points for RESTful processing. They use annotations to specify the requests types for which they can be used.
Given a request, the most suitable action method is selected and invoked.

Request Dispatched to
GET /apps/crm/users/789 HTTP/1.1
Accept: application/json
 
@Get @Produces("application/json")
com.crm.users.id.IndexController#getJson()
POST /apps/crm/users/789 HTTP/1.1
Accept: text/html
Content-Type: application/x-www-form-urlencoded
@Post @Produces("text/html")
com.crm.users.id.IndexController#editForm()
What makes a method to an action method? The method must
  • be public and not static
  • have a void return value
  • either have one or more HTTP method annotations (see below)
  • or must override an inherited action method from a parent controller class
You can choose any method name you want. (Of course you may also define as many "normal" methods in a Controller class as you want).
The following describes action method annotations in detail:

@Get, @Post, etc: HTTP method annotations

consist of these annotations: @Get, @Post, @Put @Delete, @Head, @Options or @RequestMethod.
A controller method needs at least one of these annotations to qualify as action method.
public class MyController extends Controller
{
@Get @Post @RequestMethod("TRACE") public void run()
{
...
These method annotations signal that only requests with such HTTP method(s) can be processed by the action method. In the example above the run-method only processes GET, POST or TRACE requests.

@Produces annotation

Action methods can use the @Produces annotation to specify that they only can process requests with certain Accept-Headers.
The client uses this header to specify the acceptable content types for the response body. Since different response types usually require different processing, the @Produces annotation allows to
  1. overall specify which content types can be produced by the controller
  2. and which action method should be invoked for a specific request
@Get @Produces("text/html") public void asHtml() {
... // produces a HTML response
}

@Get @Produces("application/json;q=2, application/xml") public void asData() {
... // produces a JSON or XML response
}

@Get @Produces("image/*") public void asImage() {
... // produces a image
}

The @Produces annotations specifies one or more content types. Wildcards are allowed, as are quality parameters q with values ≥ 0 to express server-side preferences of certain content types.
The client accept-header will also contain a list of (wildcarded, parameterized) content-types. The content negotiation algorithm is used to match client preferences with server capabilities to determine the actual content-type of the response.
An action method without a @Produces annotation makes the bold statement that it can handle all preferred content-types of the client.

@Consumes annotation

Action methods can use the @Consumes annotation to specify that they only can process requests with payloads of a certain content-type.
@Put @Consume("application/json") public void processJson() {
... // handle requests with a JSON body
}

@Put @Consume("image/png", "image/gif") public void processImages() {
... // handle requests with a image body
}

Action method selection

When a controller receives a request, it must select the most appropriate action method to process the request, or signal an error if it has no suitable method.
Selection starts with all action methods declared by the controller class and its superclasses. Methods declared in derived classes are given higher priority.
  1. Test: Only candidate methods pass whose HTTP method annotation(s) includes the request HTTP method.
    If there are no more candidates, selection stops with a 405 method not allowed error.
  2. Test: Only candidate methods pass which can consume the content-type of the request.
    The method either does not have a @Consumes annotation or the annotation value contains the request content-type.
    If there are no more candidates, selection stops with a 415 unsupported media type error.
  3. Test: Only candidate methods pass which can produce one of the acceptable response content types.
    An algorithm implemented by the ContentNegotiation class gives a score to each potential content type. The best scoring content type and the providing action method is selected as best match.
    If there is no match, selection stops with a 406 not acceptable error.

Action method parameters

Controller action methods can declare Java parameters. When the method is invoked, the Civilian runtime will inject argument values into those parameters. You must restrict to certain parameter types and/or use parameter annotations to specify what you want to inject. (In the following parameter is used both for Java method parameters and request parameters, so don't get confused).

Inject request parameters
All kinds of request parameters can be injected. The method parameter must be annotated with

Example? Example!
@Get public void youNameIt(
@Parameter("name") String name,
@Parameter("when") @LocaleValue Date when,
@PathParam("customerId") Integer custId,
@MatrixParam("p") @DefaultValue("0") int page,
@HeaderParam("x-eval") List<String> evalHeaders,
@CookieParam("prefs") Cookie prefs,
@CookieParam("optout") boolean optout)
{ ...
The annotation value specifies the name of the request parameter (which may or may not be the same as the method parameter name).
If a path parameter is injected, the type of the method parameter must equal the type of the PathParam.
For all other parameters the injected argument is the value of the request parameter converted to the type of the method parameter. These method parameter types are supported:
  • String (of course since request parameters are strings)
  • any class for which a Type implementation exists in the applications type library. The Type implementation will parse the string value and construct the target object value.
    The default type-library includes Types for all primitive Java types and its class counterparts, and common Date classes.
  • any class with a constructor which takes a single string argument.
  • any class with a public static method named valueOf or fromString which takes a single String parameter and returns an object of that class.
  • java.util.List, java.util.Set, java.util.SortedSet. Many request parameters can have multiple values, therefore these collection parameters will receive all values, not just the first one. If no generic type argument is given on the collection, the collection will contain string values. Else the generic type argument must be a supported simple type and the values will be converted accordingly.
  • Value
  • @CookieParam can also inject the whole Cookie object and not just the cookie value.
Conversion of parameter string values to another type is by default locale independent. But especially for parameters locale dependent parsing can be what you want. In this case add the @LocaleValue annotation.

The @DefaultValue annotation can be used to specify a default value which is injected if the request does not contain the parameter.

Inject request content
You can annotate a method parameter with @RequestContent to inject the request content:

@Put @Consumes("application/json") public void placeOrder(@RequestContent Order order)
{ ...
Conversion of the request content depends on the type of the method parameter: If the type of the method parameter is
  • java.io.InputStream an InputStream for the request content is injected
  • java.io.Reader or java.io.BufferedReader a Reader for the request content is injected
  • byte[] the raw request content is injected
  • String the decoded request content is injected
  • for any other type Civilians content serialization mechanism is used to produce an parameter instance from the request content. In this case you want to use a @Consumes annotation to restrict to content-types which can be handled by your application.

Inject response content
Action methods which return data (e.g. as XML or JSON) can use Civilians content serialization to easily marshall the data:

@Get @Produces("application/json") public void getOrder()
{
Order order = new Order();
... // fill order object with data
getResponse().write(order); // serializes to JSON
}
For a slightly more compact syntax you can use the @ResponseContent annotation to achieve the same effect. A new instance is injected into the parameter. When the action method ended and the response is not committed, the object will be written to the response:
@Get @Produces("application/json") public void getOrder(@ResponseContent Order order)
{
... // fill order object with data
}
An alternative to the @ResponseContent annotation is to derive your data class from the ResponseContent class.
@Get @Produces("application/json") public void getOrder(Order /*extends ResponseContent*/ order)
{
... // fill order object with data
}

Inject request or response objects
When a controller action method is invoked the request and response are available via getRequest() and getResponse(). Similar you can access the application, the context and other context variables.
But if you prefer you can also inject request and response objects via method parameters (no annotations needed):

@Get public void process(Request request, Response response)
{ ...

Inject multiple request parameters as Java bean
The signature of a controller action method can become crowded if it contains a lot of parameters. This especially happens if you want to process a posted HTML form with many form controls. The @BeanParam annotation allows you to inject a bean object into a controller method parameter, with the bean properties populated by request values. These properties are supported:

  • Bean setter methods. If not otherwise annotated, the request parameter with the name of the bean property will be injected into the property.
  • Other public methods, with a single argument and an annotation what to inject.
  • Public fields. If not otherwise annotated, the request parameter with the name of the field will be injected into the field.
@Post public void process(@BeanParam Data data)
{ ... }

public class Data
{
/*implicit: @Parameter("name")*/ public void setName(String s) { ... }
/*implicit: @Parameter("length")*/ public int length;
@MatrixParam("page") public void initPage(int n) { ... }
@PathParam("customerId") public Integer customeId;
@BeanParam public void setMoreData(MoreData md);
}

Build own annotations to inject custom objects
Civilian allows to build own annotations for action method parameters. Please study the Inject sample how to do this.

import org.myapp.annotations.Order;

@Post public void process(@Order Order order)
{ ...

Injection errors
Converting request parameters or content to the method parameter type may fail due syntactically invalid data. In this case the action method is not invoked, but instead a BadRequestException is thrown which will by default result in a 400 bad request error sent to the client.

You can avoid injection error by using a Value parameter. If an conversion error occurs the Value object will store the exception, else will contain the parsed value:

@Get public void process(@Parameter("page") Value<Integer> page)
{
if (!value.hasError())
{

Controller Lifecycle

The lifecycle of a controller is simple: A controller instance is created to process a single request, and then discarded.
This has the following important consequences:
  • A controller class itself must not be thread-safe.
  • A controller class can define own properties to store temporary data. The controller base class actually defines several properties, e.g. the request property.

Controller invocation

The entry point for request processing are action methods. But the call to the negotiated action method is embedded in calls to special init- and exit-methods. The exact sequence of method calls when a controller processes a request is as follows:

Controller architecture

Controller classes all derive from the Controller base class. Its also a good idea to introduce own controller base classes in your application, to let derived classes inherit common functionality.
Controller base classes can especially provide meaningful implementations of checkAccess(), init(), setCaching(), onError(e) and exit().

For example a base controller class might implement checkAccess() to test if the user is logged in and if not redirect to a login page or send a 403 forbidden response. All derived controller would automatically inherit that access control. The Security chapter discusses this topic in detail.

Another example is demonstrated by the JPA / Resource Local sample. A base controller class uses exit() to commit or rollback a JPA transaction.

Dependency injection

If you intend to use Dependency Injection (DI) in your web application, then Controller objects are the ideal targets to inject dependencies.
Controller objects are created per request. You can intercept Controller creation by providing a ControllerFactory during application startup.

Civilian itself provides factory implementations for Guice and CDI in order to use Guice or CDI dependency injection on controllers. Several samples demonstrate this feature.