Home >ESP Guide> Controllers and Actions

Quick Nav

See Also

Controllers and Actions

The ESP Model-view-controller (MVC) architecture is a pattern for designing well-structured applications and MVC applications have proven to be an effective organization for web applications. A key reason is that MVC apps separate the logic (controller), from the presentation (view) and the application's data (model). This clear partition of responsibilities, scales well and helps to create maintainable applications.

MVC controllers are the conductors of the application and they orchestrate the application's activities. Via action functions, they receive client requests and generate appropriate responses, mutating the applications data model in the process.

Example of a Controller

So what does a controller look like? Here is a partial example that has four actions:

#include "esp.h"
static void create() { 
    /* Create post here */
    redirect("list")
}
static void list() { }
}
static void edit() { 
    /* Create post here */
}
static void update() { 
    /* Update post here */
    renderView("post-edit");
}
ESP_EXPORT int esp_controller_post(HttpRoute *route, MprModule *module) 
{
    Edi     *edi;
    espDefineAction(route, "post-create", create);
    espDefineAction(route, "post-edit", edit);
    espDefineAction(route, "post-list", list);
    espDefineAction(route, "post-update", update);
    return 0;
}

Say the client issues a request for:

http://embedthis.com/app/post/create

assuming a correctly configured request route, the create action, in the post controller will be run. This will create a new blog post and then render the post-list.esp view of posts back to the client.

Routing Requests

At the heart of understanding how controllers are loaded and actions are run, is understanding Appweb routing. When Appweb receives a client request, it examines each of the request routes configured in the appweb.conf configuration file, until it finds a matching route for the request URI. The selected route will then break the URI into tokens and save the token values as request parameters.

For example: consider the URI format:

http://embedthis.com/APP/CONTROLLER/ACTION/ID

In this example, APP is the (optional) name of the application, CONTROLLER is the controller name, ACTION is the name of the action method to run and ID is a selector for an element in the Model. When the request URI is tokenized, the Appweb router will extract the controller name and then use this name to load the appropriate controller to service the request.

RESTful Routes

A RESTful approach to designing URI routes is also often used where different controller actions are linked to specific routes. The benefit being being simple, readable, RESTful URIs.

NameMethodPatternAction
initGET/CONTROLLER/init$init
createPOST/CONTROLLER(/)*$create
editGET/CONTROLLER/edit$edit
showGET/CONTROLLER$show
updatePUT/CONTROLLER$update
destroyDELETE/CONTROLLER$destroy
default*/CONTROLLER/{action}$cmd-${action}

In either case, RESTFul or not, the request URI is broken into tokens that specify the controller and action to invoke to service the request. When a request is received, ESP will create an instance of the controller and invoke the requested action method.

Processing Flow

Appweb processes requests in stages:

  1. Decode the URI and web request
  2. Route an incoming request to the request handler (ESP)
  3. If the request is for a web page, run that and render a response
  4. Otherwise if the request is routed to a controller, load the controller
  5. Run the specified controller action determined by the request route pattern
  6. Optionally render a response view to the client

Configuring Controllers

Controllers may be configured two ways:

MVC Configuration

MVC applications are typically created via the esp generator command. The esp command is used to create a bare MVC application with a directory for controllers, views and databases. It is also used generate an appweb.conf configuration file to use when you run Appweb to host your application. By default, this configuration file will have an EspApp directive configured for your application. This directive creates a top level route for the MVC application and then a set of routes for each of the RESTful verbs: create, destroy, edit, list, show and update.

EspApp Prefix Dir RouteSet Database

The EspApp directive creates routes that intercept request URIs that begin with the given prefix and directs them toward the MVC application in the specified Directory. The RouteSet is typically "restful". Lastly, an optional database may be pre-opened for the MVC application. For example:

EspApp /store ./applications/store restful mdb://app/db/store.mdb

Stand-Alone Controllers

Sometimes, you may wish to have a controller without a full MVC application. You can do this by using a set of Appweb directives.

<Route ^/app/{controller}(/)*$ >
    Name /app/*/list
    AddHandler espHandler
    EspDir controllers ./controllers
    Source test.c
    Target run $1-list
</Route>

This set of directives will create a route that will invoke the controller controllers/test.c. If the /app/test/ URI is invoked, the list action function will be invoked. The EspDir directive nominates the directory to contain the controllers. By default this is configured to be {Documents}/controllers. The Target directive defines how the action function name to invoke. Note: This usage is less typical than using the MVC pattern.

Actions

Actions are where the controller does its work. In ESP, actions are simple "C" functions and thus they need to be registered before use. This is done in the controller initialization function. The initialization function should be named: "esp_controller_NAME", where NAME is the unique name of the controller. The first time the controller is invoked, the controller library will be loaded and the initialization function will be run. Typically, the initialization function will then call espDefineAction to bind action functions to route actions.

Missing Actions

When responding to a request, if the required action is not found, ESP will look for an action called "CONTROLLER-missing". If that is not defined in the controller, it will look for a global "missing" function. Otherwise it will respond with a HTTP 404 error indicating that the required action could not be found.

Processing the Request

The controller action can perform any processing it desires. There are no real restrictions except you don't want to block for too long without giving the client some feedback. Because Appweb is multithreaded, you can block. In that case, the server will continue to run and serve other requests. However, note that threads are a limited resource. It may be better to use non-blocking techniques such as async processing.

Async Processing

An action may service a long-running request without blocking, by responding in pieces. An action function may return without completing the request. Normally, ESP will automatically finalize the request when the action returns. To prevent this, call dontAutoFinalize to tell ESP not to finalize the request and to keep the connection and response open. At anytime and from any other code, you may then call finalize to complete the request. To force output to the client, without finalizing, use flush.

For example:

static void second(HttpConn *conn) {
    setConn(conn);
    render("World\n");
    finalize();
}
static void first() {
    dontAutoFinalize();
    render("Hello ");
    flush();
    setTimeout(second, 5000, getConn());
}

This example will print "Hello ", then wait five seconds and then print "World". Note that the request is held open, but Appweb is not blocked in any thread. The call to setTimeout will arrange to have the Appweb event loop invoke second after five seconds have elapsed. This pattern is a highly efficient in its use of system resources and scales very well.

Loading and Caching Controllers

Before a controller can run, it must first be compiled and linked into a loadable library. On Windows, this will be a DLL, on Unix, it will be a shared library with a ".so" extension. On VxWorks it will be a loadable task module.

The compilation of controllers into libraries happens automatically if a compiler is installed on the system and if the EspUpdate directive is enabled. If so, when a request is received, ESP will compile and link the controller in to a library and save the result into the ESP cache directory for future use. After servicing the first request for the controller, the controller code is retained in memory and the controller will not be reloaded unless the source code is modified. If Appweb is rebooted, the cached library will be reloaded without recompiling. This provides two levels of caching: in-memory and on-disk as a shared library.

Development and Production Modes

When a request for a controller is received, ESP will test if the source code has been updated to determine if the controller must be recompiled. If the source has been changed, Appweb will wait for all requests that are using the already loaded controller, to gracefully complete. When no requests are using the old controller version, Appweb will unload the controller, and ESP will recompile the updated controller source and create a new shared library that will then be loaded and the request servicing resumed.

If Appweb was configured and built in debug mode, the default value for EspUpdate will be on. If Appweb was built in release mode, the default value is off. In release mode is it common practice to lock down the compiled controllers and not auto-recompile once deployed.

Controller Context

ESP establishes a request context for the controller before invoking the controller action. The top level of the context is represented by the HttpConn connection object. From this, all other request information can be reached, including the:

ESP Short-Form API

When ESP invokes a controller action or a ESP web page, ESP defines the current HttpConn connection object in Thread-Local storage. ESP also defines a terse, short-form, API that uses the current connection object to provide context for the API. When using this API, explicit access to the connection object should not typically be required. The ESP short-form API should cover 95% of requirements for action processing.

Explicit Connection Access

If explicit access to the connection object is required, action functions may define a connection argument as which is passed into all actions.

static void ACTION(HttpConn *conn) {
    /* Use the conn reference here */
}

Alternatively, the connection object can can be retrieved using the getConn API.

Navigating the Connection Object

The HttpConn object represents the current TCP/IP connection. By using HTTP KeepAlive, the connection may be utilized for multiple requests. The fields of the HttpConn object are public and can be accessed and navigated.

HttpConn PropertyPurpose
rxReference to the HttpRx receive object
txReference to the HttpTx transmit object
hostReference to the HttpHost object
httpReference to the Http object
endpointReference to the HttpEndpoint transmit object
limitsReference to the HttpLimits object
ipRemote client IP address
portRemote client port

Navigating the Receive Object

The HttpRx object represents the receive side of the HTTP protocol. On the server, it holds state regarding the client HTTP request. The fields of the HttpRx object are public and can be accessed and navigated.

HttpRx PropertyPurpose
methodHTTP request method
uriCurrent URI (may be rewritten)
pathInfoPath portion of the URI after the scriptName
scriptNameScriptName portion of the URI
lengthContent length
routeReference to current HttpRoute object
paramsRequest params (query, form and route parameters)
filesUploaded files

Navigating the Tx Object

The HttpTx object represents the transmit side of the HTTP protocol. On the server, it holds state regarding the response to the client. The fields of the HttpTx object are public and can be accessed and navigated.

HttpTx PropertyPurpose
filenameName of the real file being served
extFilename extension
handlerRequest handler object
lengthResponse content length
statusResponse HTTP status
headersResponse HTTP headers

Sample of ESP API

Here are a few of the ESP APIs that may be used inside controller actions:

Method / Property Description
addHeader Add a response HTTP header.
createSession Enable session control.
destroySession Destroy a session.
dontAutoFinalize Don't automatically finalize output when the action returns. Useful for async actions.
error Send an error flash message to the next web page.
inform Send an informational flash message to the next web page.
param Get a request parameter value.
redirect Redirect the client to a new URI.
render Render the text data back to the client.
renderFile Render a file's contents back to the client.
setContentType Set the response content type.
setCookie Define a cookie to include in the response.
setSessionVar Set a variable in session state storage.
uri Make a URI from parameters.

Request Parameters

ESP will collect request query, form data and route parameters into one params object which is accessible to actions via the param API. Each query key/value pair and all request form elements posted by the client will become a properties of the params object. When routing the request, Appweb will tokenize the URI and create parameters for each positional token in the URI. The Controller name and Action are defined as the parameters: controller and token.

Rendering Views

After processing the request, the controller is responsible for rendering a response back to the client. The controller can choose how to respond. It may explicitly create the response body by calling render to generate HTML. Alternatively, the action may call renderView to response with a view web page. If the action method does not explicitly generate any response, ESP will invoke a view with the same name as the action method.

Generating Controllers and Actions

If you are creating an MVC application, you may use the ESP application generator, called esp to make it quick and easy to create controllers, actions and controller scaffolds. To generate a new controller, run:

esp generate CONTROLLER ACTIONS...

CONTROLLER is the controller name. ACTIONS... are the names of the actions you want to generate. This command will create the controller source file under the controllers directory. The controller source will contain empty functions for each of the requested actions. You can edit the controller source to meet your needs.

© Embedthis Software LLC, 2003-2013. All rights reserved. Embedthis, Appweb, ESP, Ejscript and Embedthis GoAhead are trademarks of Embedthis Software LLC.