In this article, we review the Commerce Controller types to identify the purpose behind each controller type and assist with solution design in order to implement Commerce Controllers correctly in your Commerce Engine Plugin projects.
Commerce Controller Types
Entity Controllers
Entity Controllers, e.g. CartsController, implement Get methods and should only retrieve data, returning specific entities, lists of entities, or managed lists of entities. They should not manipulate data in the commerce databases or trigger behaviour within the Commerce Engine.
The following code snippet shows the Get Carts endpoint in the CartsController from the Carts Plugin.
public async Task<IEnumerable<Cart>> Get() { CommerceList<Cart> commerceList = await Command<FindEntitiesInListCommand>()?.Process<Cart>(CurrentContext, CommerceEntity.ListName<Cart>(), 0, int.MaxValue); return (commerceList?.Items.ToList()) ?? new List<Cart>(); }
Api and CommerceOps Controllers
Api and CommerceOps Controllers implement methods that return non-OData entities only. The methods/endpoints of these controllers are routed via the ‘/api’ and ‘/commerceops’ URL segments, where the api routing is intended for website consumption while the commerceops routing is intended for DevOps consumption.
The following code snippet shows the GetBulkPrices endpoint in the ApiController from the Catalog plugin.
public async Task<IActionResult> GetBulkPrices([FromBody] ODataActionParameters value) { if (!ModelState.IsValid) return new BadRequestObjectResult(ModelState); if (!value.ContainsKey("itemIds") || !(value["itemIds"] is JArray)) return new BadRequestObjectResult(value); var jarray = (JArray)value["itemIds"]; IEnumerable<SellableItemPricing> bulkPrices = await Command<GetBulkPricesCommand>().Process(CurrentContext, jarray?.ToObject<IEnumerable<string>>()); return new ObjectResult(bulkPrices); }
Commands Controllers
Commands Controllers implement OData actions for manipulating data through Commands. They should only return Command OData entities, not commerce entities like carts.
The command object contains information about the execution of the command, i.e. ResponseCode, Status, isCancelled, isCompleted, etc.
Some APIs do not wait for the command to complete and will return the command with the Status property as WaitingForActivation. These long running commands can be followed up on using the CheckCommandStatus() API with taskId parameter. See the Check Long Running Command Status API in the Sitecore DevOps postman collection.
The following code snippet shows the AddFederatedPayment endpoint in the CommandsController from the Payments plugin.
public async Task<IActionResult> AddFederatedPayment([FromBody] ODataActionParameters value) { if (!ModelState.IsValid || value == null) return new BadRequestObjectResult(ModelState); if (!value.ContainsKey("cartId") || (string.IsNullOrEmpty(value["cartId"]?.ToString()) || !value.ContainsKey("payment")) || string.IsNullOrEmpty(value["payment"]?.ToString())) return new BadRequestObjectResult(value); var cartId = value["cartId"].ToString(); var paymentComponent = JsonConvert.DeserializeObject<FederatedPaymentComponent>(value["payment"].ToString()); var command = Command<AddPaymentsCommand>(); await command.Process(CurrentContext, cartId, new List<PaymentComponent>() { paymentComponent }); return new ObjectResult(command); }
FAQ
How do I update an existing controller endpoint from any of the Commerce platform plugins?
The short answer is, you can’t modify or unregister a controller endpoint.
Whether you are looking to change the signature, the OData parameters from the request body, or any of the underlying code logic of an existing controller endpoint, you will need to create a new controller endpoint and ensure that you swap out any code that is calling the original endpoint for your custom endpoint.
Why can I call my endpoint via Postman, but it’s not available in the Service Proxy?
There are two possible reasons for this.
The first reason may be that the Service Proxy has not been regenerated since implementing the endpoint.
The second possible reason is that the endpoint has not been registered via the ConfigureServiceApiBlock or ConfigureOpsServiceApiBlock, depending on whether the endpoint should be exposed to the api or commerceops segments respectively.
Summary
While there’s nothing preventing developers from incorrectly using the Commerce Controllers, these coding standards are effectively the recommended practices that will contribute to the long term maintainability of your Sitecore Commerce solutions.