Controllers and Actions
Ejscript controllers manage the application and respond to client requests. They are the conductors of the application and orchestrate the application's activities.
Processing Flow
Ejscript controllers process requests in stages:
- Decode the URI and web request
- Route an incoming request to the appropriate controller
- Invoke the requested action
- Access and update the application state
- Render a response view to the client
When a request is received, Ejscript will parse and decode the request before routing to the appropriate Controller where it will create an instance of the controller class and invoke the requested action method.
Anatomy of a Controller
So what does a controller look like?
public class CartController extends ApplicationController { public var product: Product action function index() { renderView("list") } action function create() { product = new Product renderView("edit") } action function edit() { /* Read a product from the database */ product = Product.find(params.id) } action function list() { /* Will render list.ejs by default */ } action function update() { product = Product.find(params.id) /* Update the product based on user input */ if (product.saveUpdate(params.product)) { inform("Product updated successfully.") redirect("list") } } }
This is a simplified controller — definitely not production code as it does not validate user input. This controller provides five actions which are decorated by an action qualifier. This qualifier makes a JavaScript function visible as a controller action method.
If the client issues a request for:
https://www.embedthis.com/myApp/cart/create
The create action will be invoked. It will create a new product and then render the edit.ejs view back to the client.
Actions
Actions are where the controller does its work. In Ejscript, actions are simple JavaScript functions prefixed with an action qualifier. They don't need to be registered or otherwise configured before use. When Ejscript receives a request from the client, it will parse the request and invoke the request action specified in the URL.
When a request is received, the controller framework looks for an action method of the required name. If one is not found, it invokes the missing action method which renders a default error message back to the client.
Controller Context
Ejscript establishes a request context for the controller before invoking the action. This consists of the following objects which are visible to actions:
- application object — represents your application.
- host — describes the web server hosting the application.
- request object — describes the client HTTP request.
- response object — describes the client HTTP response.
- params — request form data
- flash — informational messages to pass to the next action (and view)
- session object — session state information.
The controller can add to this context by declaring public variables in the controller class or the ApplicationController which is the base class for all controllers. Views can automatically access any such public controller variables.
Processing the Request
The action method 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.
Actions often retrieve a database record via the Model and store an object representing the model in a public variable for easy access by the view. NOTE: the controller is not persistent across client requests.
Model Data
Ejscript provides an Object Relational Mapping (ORM) layer for accessing database data via convenient JavaScript objects. The ORM layer is a collection of database Model classes for the application. controllers use this layer to access the database via these Model classes. A typical paradigm is for action methods to read a database record and store the record in a public controller property. The view can then simply access the database data via this property.
For more details, please see Database Models.
Form Data
HTTP Form data is marshalled by Ejscript into the params object which is accessible to actions. Each HTML input element posted by the client will become a property of params. Ejscript can also organize form data into nested objects. If an input element name contains a period, then Ejscript will create nested objects to match the input element name.
Rendering Views
After processing the request, the controller is responsible for rendering a view back to the client. If the action method does not explicitly render a view, Ejscript will look for a view with the same name as the action method.
In the prior example, the edit action did not call any render function, so the framework will look for, and if found, run a view views/cart/edit.ejs.
Generating Controllers and Actions
Ejscript includes an an application generator, called mvc to make it quick and easy to create controllers, actions and controller scaffolds. To generate a new controller, run:
mvc generate controller NAMES...
NAMES... are the names of the controllers you want to generate. This command will create controller source files under the controllers directory. Each controller will contain an empty controller class with an empty index action function. You can edit the controller source to meet your needs.
When you create a controller, you can also (optionally) generate a set of named action functions. To generate actions when you create a controller, run:
mvc generate controller NAME ACTIONS...
If ACTIONS are provided, action functions and views pages will be created for the given names.
Generating Scaffolds
A scaffold is a generated set of actions and views that provide add, edit, and list functionality for a database model. Scaffolds are useful to quickly generate prototype web pages and actions for managing a database table. To generate a scaffold:
mvc generate scaffold MODEL [field:type ...]
When creating a scaffold for a database model, mvc will create a database model, database migration, controller and edit and list view pages.
If field:type pairs are supplied, the database migration will include code to create a column for each specified field of the requested type. The valid database types are: binary, boolean, date, datetime, decimal, float, integer, number, string, text, time, timestamp.
The scaffold will include an edit action and view that provides add and edit capability. The list action and view, provides the ability to list the database table rows and select an entry to edit.
Controller Class
The Controller class has a large set of methods and properties. See the Ejscript Library for full details. Some of the more important properties:
Property | Description |
---|---|
actionName | Name of the action method being run. |
application | Application global data. |
controllerName | Name of the controller method being run. |
flash | Lower case name of the controller. |
flash | Flash message object for the next view. |
host | HTTP host object. |
params | Form parameters object. |
request | HTTP request object. |
response | HTTP response object. |
session | Session state information. |
The controller class provides the following methods:
Method / Property | Description |
---|---|
cache | Add a cache-control HTTP header. |
createSession | Enable session control. |
destroySession | Destroy a session. |
discardOutput | Discard all prior buffered output. |
error | Send an error response back to the client. |
escapeHtml | Transform a string to be safe for output in a HTML web page. |
html | HTML encode the arguments. |
inform | Send a positive notification to the client in the flash area. |
makeUrl | Make a URL suitable for invoking actions. |
redirect | Redirect the client to a new URL. |
render | Render the raw arguments back to the client. |
renderFile | Render a file's contents back to the client. |
renderRaw | Render raw data. |
setCookie | Define a cookie to include in the response. |
statusMessage | Send a status message to any registered view controls. |
unescapeHtml | Transform an escaped string back to its original contents. |
warn | Send a warning message to the client in the flash area. |
write | Write text back to the client. |
writeHtml | Write HTML escaped data back to the client. |
Filters
Filters enable a controller to perform processing before and after actions are run. Filters are useful to do authentication and logging. Filters run in the same context as an action method and can access the controller instance and the Request, Response, Http, session and flash objects.
To run a filter before the action method, use the beforeFilter method. This should be run in the constructor for the controller. For example:
public class TaskController extends BaseController { function TaskController() { beforeFilter(authorize, { except: "login"}) } private function authorize() { if (params.username != "goodname" && params.password != "abracadabra") { error("Invalid Login") redirect("login") } }
This will cause the authorize to run before any action method except for the login action. The authorize function can test the user name and password and if not authorized, it can fail the request and redirect the user back to the login page. Filters can be run after the request by using the afterFilter method.
The filter methods take an options object as the second parameter. Two option properties are defined:
- except — Run filter for all actions except the nominated action.
- only — Run filter only for the specified actions.
When specifying the argument to except and only, you can provide either a single action name as a string or an array of action names.