Handlers
Describes how to use handlers in Optimizely Configured Commerce.
Handlers are objects that are responsible for the heavy lifting of objects and processing of business logic. In the Optimizely Configured Commerce architecture, handlers take control right after the REST API receives and initially processes the request. Each handler is part of a larger handler chain that operates on specific objects. When a handler chain is executed, each handler in the chain is executed in a configured order until no more handlers are available in the chain. Below are some existing handler chains that showcase just some of the use cases for handlers.
GetCartHandler
chain – This chain retrieves and returns the cart object for the currently authenticated user or using a specific cart identifier. This chain also includes handlers that retrieve cart-related data, like cart lines, shipping costs, taxes, and available carriers. This chain is executed indirectly throughout the Storefront, including when a user visits the Cart page, goes to the Checkout Address page, and proceeds to the Review and Pay page.GetProductHandler
chain – This chain retrieves a product given a product identifier. This chain is executed when a user visits the Product Detail page or searches for a product on the Quick Order page.
Handler chains make it possible to add new handlers and insert new business logic into the chain. You can read further to learn how to add a new handler to a handler chain.
Work with handlers
Notice that the handlers do not directly implement the IHandler<,> interface. Instead, they inherit from the HandlerBase class. This base class directly implements the IHandler<,> interface while providing utility methods for returning error results and passing execution to the next handler. As a best practice, all handlers should inherit from the HandlerBase class. The association to a handler chain is complete at this point. However, the handler requires a bit more configuration to function within Configured Commerce.
// These two handlers are part of the same handler chain
// because they both declare the same parameter and result
// object types.
public class GetCart : HandlerBase<GetCartParameter, GetCartResult>
public class GetCartLines : HandlerBase<GetCartParameter, GetCartResult>
// This handler is part of a different handler chain because
// it declares different parameter and result object types
// than the two handlers above.
public
A handler is associated with a handler chain according to the parameter and result object types (TIn and TOut). All handlers with the same object types will be executed within the same handler chain. Below is a code sample explaining the association further.
Handlers are ordered in ascending order within a handler chain using the values specified by each handler in the "Order" property. Handler chains start with the handler with the lowest order value. By default, all handler chains have at least one handler with an order value of 100. From there, additional handlers usually increment the order by 100. For example, if a handler chain has three handlers, the order values would be 100, 200, and 300. This ordering scheme lets you both insert a new handler in between existing handlers or add a new handler to the start or end of a handler chain. To insert a new handler into the second position in the handler chain, the handler can be given an order value between 101 and 199. To add the handler to the start or end of the handler chain, the handler can be given an order less than 100 or greater than 300, respectively.
The IHandler<TIn, TOut> interface requires the handler to implement the "Execute" method, specify handler order using the "Order" property, and associate the handler with a handler chain. The "Execute" method is the sole entry point into a handler. This method also contains all of the business logic used by the handler to complete its work. The "Order" property is used to specify the ordering of the handler within the handler chain.
- Implement the IHandler<TIn, TOut> interface
- Be decorated with the DependencyName attribute
- Return a result from the "Execute" method
- Specify an order via the "Order" property
Add a handler
Handlers serve as an extension point because you can add new handlers to any handler chain. This lets you add new or change existing functionality. For example, to the GetCartHandler chain you could add a handler that selectively displays taxes based on a custom property. To add a new handler to an existing handler chain, you must correctly implement and configure the new handler.
The final configuration involves the dependency locator. All handlers must be decorated with the DependencyName attribute. This allows the handler to be retrieved by name via the dependency locator.
Once you have configured the new handler, you can build the solution and run the application. The next time the handler chain executes, your new handler should be included in the chain. To help explain the handler chain further, the following section walks through adding a new handler to a handler chain.
The following section adds a new handler to the GetCartHandler chain. This new handler selectively returns the cart taxes based on the current user's role.
Preconditions
- Configured Commerce SDK installed
Steps
-
In your Extensions project, create a new handler class.
public
class
ShowTaxAndShippingForBuyer3``{``}
-
Implement the IHandler<TIn, TOut> interface by inheriting from HandlerBase<TIn, TOut>. For the TIn and TOut parameter and result object types, specify GetCartParameter and GetCartResult, respectively. Remember, this is how a handler is associated with a handler chain. It is important to specify the correct types. Also remember that the HandlerBase base class provides utility methods for returning error results. You can see these utility methods in action in later steps.
public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { }
-
Decorate the handler with the DependencyName attribute. Use the name of the class for the dependency name.
[DependencyName(nameof(ShowTaxAndShippingForBuyer3))] public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { }
-
Specify a value for the Order property. Remember, all handler chains start at an order of 100. Within the handler chain, all handlers provided by ISC Cloud increment the order by 100.
public override int Order => 3000;
-
Implement the Execute method. Remember, this is the sole entry point to the handler. Additionally, this method must either continue the handler chain or break it. Both methods are explained in the sample.
This handler logic requires the Order Management > Cart > Show Tax & Shipping Amount in Cart setting to be set to "Yes". This precondition is only for this specific example. You can add a handler regardless of the value of this setting.
public override GetCartResult Execute(IUnitOfWork unitOfWork, GetCartParameter parameter, GetCartResult result) { if (result.Cart == null) { // This is one of the utility methods for returning an error result // provided by the base class. These error results will be propagated // all the way up to the REST API and returned as a result. // Any following handlers will not execute. return this.CreateErrorServiceResult(result, SubCode.CartServiceCartNotFound, "A cart could not be found."); } result.ShowTaxAndShipping = result.UserRoles != null && result.UserRoles.Split(',').Contains(BuiltInRoles.Buyer3); // Once your handler is finished with its work, if // you execute the next handler, execution will continue // in the handler chain with control being handed over to // the next handler. return this.NextHandler.Execute(unitOfWork, parameter, result); // Returning the result breaks the handler chain. // Any following handlers will not execute. return result; }
For reference, below is the completed handler class.
[DependencyName(nameof(ShowTaxAndShippingForBuyer3))] public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { public override GetCartResult Execute(IUnitOfWork unitOfWork, GetCartParameter parameter, GetCartResult result) { result.ShowTaxAndShipping = result.UserRoles != null && result.UserRoles.Split(',').Contains(BuiltInRoles.Buyer3); return this.NextHandler.Execute(unitOfWork, parameter, result); } public override int Order => 3000; }
-
Build your solution.
If the rest of your application is set up correctly, the next time you add a product to the cart and go to the Cart page, you may or may not see the shipping and taxes (depending on your role).
Remove or replace handlers
In previous versions, developers could derive new view models, derive new .NET controllers from existing functionality and override methods to change the behavior of the platform. Configured Commerce 4.0 is prescriptive when it comes to extending the platform. Module handlers are a primary extension point to the platform where developers can add additional business logic and provide additional data to the objects used for the interface.
To remove or replace a handler, give it the same dependency name as an existing handler. By Inversion of Control within Configured Commerce you can create instance that implements HandlerBase with AddAccountParameter and AddAccountResult and same dependency name, your custom class will override the standard Configured Commerce handler. Creating a custom class that derives from HandlerBase and using the same dependency name will void the need for using the Order property.
Note
You cannot "remove" a handler, but you can create an implementation of it that does nothing.
Updated over 1 year ago