Configured Commerce modules
Describes the WebAPI controller and the various handlers used when working with Optimizely Configured Commerce.
WebAPI controller
The WebAPI controllers, built by Optimizely for each of the Configured Commerce modules, serve as the endpoint for the RESTful based services. The WebAPI controller contains the RoutePrefix which identifies its RESTful path.
Configured Commerce modules that expose a RESTful API layer must derive from BaseApiController. Deriving from BaseApiController allows WebAPI controllers to understand and interact with context, cookies, and standardize a properties collection on all JSON objects. This properties collection stores any extensions to the JSON object with a key/value pair.
For example, use the properties collection to store custom properties from the Management Console.
Once the WebAPI controller derives from BaseApiController it contains methods that map to the service methods in typescript services and thus, the HTTP verbs. Each WebAPI controller method is decorated with a Route attribute which, in combination with RoutePrefix, provides a full path to the RESTful service.
The Parameter Object and Result Object patterns are used to clump common objects together for consistent calls to and from service methods. This means that parameters are clumped using the Parameter Object pattern and objects returned from a function use the Result Object pattern.
The Data Mapper design pattern is used to map the service result object to the WebAPI return object. The data mapper will also map the WebAPI parameters to the service parameter objects.
Module services
The main purpose of services within a module is to operate the handler factory. This is where the Chain of Responsibility is executed. The context is required on all service calls.
Module handlers
At its core, Configured Commerce handlers are responsible for the heavy lifting of objects and processing the business logic. Handlers derive from HandlerBase which uses generics to leverage the Parameter Object and Result Object pattern.
The platform dynamically loads the handlers using the DependencyName. This attribute is required when replacing existing handlers or adding new handlers to the chain of responsibility.
The Order property on a handler represents the order in the each handler is executed in the chain. The lower the order number the higher precedence in which that handler is executed. The default order for all Configured Commerce handlers is 500.
Handlers will always override the Execute method which will then define all of the core business logic of that handler.
Add a new handler
The following considerations need to be made when adding handlers to the chain. By adding a handler to the chain of responsibly the base code will execute and then execute the next handler in the chain. Unless of course, if the order has been set lower than 500.
Make sure to include the DependencyName attribute on all handlers. The name must be unique if the intent is to add a new handler to the chain however if should be the same as the base handler if the desired result is to override the base handler.
Breaking the handler chain can be done one of two ways:
- By returning the result instead of calling this.NextHandler.Execute()
- By setting the next handler to be a NullHandler
- For example, this.RegisterNext(new NullHandler(TParameter, TResult>())
Override a handler
The following considerations need to be made when overriding handlers. The recommendation is to always add a handler to the chain unless the base functionality has to change for a given implementation.
Set the DependencyName attribute on the handler class to the same name as the handler that you would like to override.
- The reason for naming it the same name as the base handler is that if you do not set the same name then the platform interprets two instances of the same handler. One with any changes that have been made and the other that is the base handler.
- When multiple handlers with the same DependencyName are registered, the one with the lower dependency order will be used by the platform. Those that are in the namespace that starts with "Insite" are registered with an order of 999. Handlers where the namespace does not start with "Insite" are registered with an order of 500. Dependency order can be explicitly set by adding the DependencyOrder attribute on the handler.
Pass data to and from handlers
Every REST call can accept a dictionary of data in the properties field. This is defined on BaseModel so any methods which takes objects derived from it will expose a properties field in typescript.
Other rest calls which pass query string data also implicitly accept a properties dictionary.
For example to pass extra data to the /products method, add a query string value in the form
&properties={'extradata':'test','extradata2':'test2' }
These property values will be automatically passed through to the handlers, in the Properties dictionary on the ParameterBase derived service parameter object. It is possible to send custom data on the query string as any arbitrary parameter and pick these up in a custom mapper. However, this requires custom code and overrides and should be avoided whenever possible in order to ease upgrades. In addition, many of the resulting objects that have child objects (usually in collections) will automatically have the Properties dictionary on each child object populated with the properties data from corresponding service result child object.
For example. WishListModel has a child collection of WishListLineModel objects with a Properties collection on each one and these will automatically get the properties dictionaries from the WishListLineResult objects created by the handler.
To return arbitrary data from a rest call, put data in the Properties collection with Properties. Add and it will be automatically returned to the client. To pass back complex objects, use the AddObjectToResultProperties method in HandlerBase to get the correct serialization this.AddObjectToResultProperties(result, "extradata", new { Something = "test", SomethinglElse = "test2" });
On the client deserialize these complex objects with JSON.parse var extradata = JSON.parse(data.properties["extradata"]);
For more details on handlers, see the Work with handlers article.
Updated over 1 year ago