Working with SGJC Controllers
If you are starting a new storefront implementation, Salesforce recommends using controllers instead of pipelines for your application code.
What are controllers?
Controllers are server-side scripts that handle storefront requests. Controllers manage the flow of control in your application, and create instances of models and views to process each storefront request and generate an appropriate response. For example, clicking a category menu item or entering a search term triggers a controller that renders a page.
Controllers are written in JavaScript and Salesforce B2C Commerce script. They must conform to the CommonJS module standard.
The file extension of a controller can be
either .ds or .js. Controllers must be located in the
controllers
folder at the top level of the cartridge.
Exported methods for controllers must be explicitly made public to be
available to handle storefront requests.
Example: SGJC Controller Hello.Js
/**
* A hello world controller.
*
* @module controllers/Hello
*/
exports.World = function(){
response.getWriter().println('Hello World!');
};
exports.World.public = true;
- use
require
to import script modules: Any B2C Commerce script can be made into a CommonJS module and required by a controller. - use
require
to import B2C Commerce packages, instead of the importPackages method. This is the best practice way of referencing additional functionality from a controller. You can also use the require method to import a single B2C Commerce class. For example:var rootFolder = require('dw/content/ContentMgr').getSiteLibrary().root;
While it isn't best practice, controllers can also:
- call other controllers. It isn't recommended that controllers call each other, because controller functionality should be self-contained to avoid circular dependencies. In some cases, however, such as calling non-public controllers during the checkout process, it is unavoidable.
- call pipelets: calling pipelets from within a controller is strongly discouraged. It's allowed while there are still pipelets that don't have equivalent B2C Commerce script methods, but will not be supported in future.
- import packages. This is discouraged as it doesn't use standard JavaScript patterns, but is Rhino-specific.
- call pipelines that don't end in interaction continue nodes. This
is only intended for use with existing link cartridges and is highly
discouraged for general development. Note: It isn't currently possible to call pipelines that end in interaction continue nodes.
Tools
You can use any IDE with a JavaScript editor to develop controllers. However, to upload code, you must either use UX Studio or an upload utility. Upload utilities are available from the Salesforce Commerce Cloud community repositories in GitHUb.
You can also use any standard JavaScript tools, including linters, static code analysis tools.
Request URLs
controller-function
. For example, the request
URL for the Hello.js controller World function looks like:
https://localhost/on/demandware.store/Sites-SiteGenesis-Site/default/Hello-World
See also: B2C Commerce URL Syntax Without SEO.
Reserved Names for Controllers
B2C Commerce has several system names that are reserved and can't be used for custom controllers and their functions. For a full list, see System Pipelines and Controllers.
Secure Requests
You
might need to secure access to the exported functions of the controller. A
controller function is only called if it has a public
property that is set to true
. All other functions that do
not have this property are ignored by B2C Commerce and lead to a security
exception if an attempt is made to call them using HTTP or any other
external protocol.
Example: accessible controller
exports.World = function() {};
exports.World.public = true;
Guards
For controllers, SiteGenesis uses the concept of guards to wrap controller functions when they are exported. The functions in the guard module act as a request filter and let you specify multiple levels of access to controller functionality, such as:
- require HTTPS
- require or forbid a certain HTTP method, like GET, POST,...
- require that the current customer is logged in
- require that the function might only be executed as remote include, but not as top-level request
A guard is a wrapper function that encapsulates a delegate function and only invokes it if its guard condition is satisfied. The guard conditions represent single preconditions that can also be combined with each other. The conditions use the B2C Commerce API to determine the properties of the current request and logic to determine whether to continue request processing or throw an error.
Example: require GET
exports.StartCheckout = guard.ensure(['get'], startCheckout);
Example: require HTTPS and that the customer is logged in.
exports.EditProfile = guard.ensure(['get', 'https', 'loggedIn'], editProfile);
Global Variables
Controllers do not have access to the Pipeline Dictionary, but they do have access to global variables, such as session, via the TopLevel package Global class.
Variable |
Type |
Description and example |
customer |
dw.customer.Customer |
The current customer, either an anonymous customer or an authenticated customer with an account. |
request |
dw.system.Request |
The current HTTP request
|
response |
dw.system.Response |
The current HTTP response
|
session |
dw.system.Session |
The current session.
|
Request Parameters and Page Meta Data
Expression |
Type |
Description and example |
request.httpParameterMap |
dw.web.HttpParameterMap |
The replacement for the CurrentHttpParameterMap variable in the Pipeline Dictionary. |
request.pageMetaData |
dw.web.PageMetaData |
The replacement for the CurrentPageMetaData variable in the Pipeline Dictionary |
render: function (templateName) {
...
this.CurrentForms = session.forms;
this.CurrentHttpParameterMap = request.httpParameterMap;
this.CurrentCustomer = customer;
this.CurrentSession = session;
this.CurrentPageMetaData = request.pageMetaData;
this.CurrentRequest = request;
try {
ISML.renderTemplate(templateName, this);
} catch (e) {
dw.system.Logger.error('Error while rendering template ' + templateName);
throw e;
}
return this;
}});
Back to top.
Debugging
Almost any JavaScript-related error
gets logged in the customerror_* log
files, including
errors from B2C Commerce scripts and controllers. The error_*
log files contain Java-related errors on B2C Commerce, which are probably only
useful for B2C Commerce users.
Error.js
, that services uncaught errors. By
default, B2C Commerce returns a 410 error, which indicates that the resource is
no longer available. For those who prefer 404, it's fine to use as long as
the related ISML template doesn't include <isslot>
,
<iscomponent>
, or
<isinclude>
tags. For genuine server errors, it is
more truthful to return a 500 error, but most merchants prefer not to send
back this level of detail to their customers. When you want to handle
these errors explicitly, you can use try/catch
statements
to do so.
Best Practices
Performance
Only import modules when you need them, not at the top of a module.
Using Controllers with OCAPI
If you intend to build a mobile or native app in addition to your storefront, you can use hooks to create modules that can be used by both your desktop storefront and mobile app.
Jobs
You cannot create jobs with controllers.