Integrating EPiServer 7 MVC and Commerce 1 R3 - Part 3: Creating an Entry Action Filter
When developing out our EPiServer 7 MVC and Commerce site, we needed to have some way to ensure that the requested Entry
is valid and exists in our catalog. In our solution, since we have multiple actions in our ProductDetailPageController
, we created an action filter to validate {entry}
parameter in our route, instead of repeating the code for every action.
This is part of a multi-post series regarding integrating EPiServer Commerce 1 R3 with an EPiServer 7 MVC site. In this post, I'll discuss the action filter we created to validate the requested Entry
.
Our [ValidateEntry] action filter
Since we use this action filter to ensure the requested Entry
is valid and present for our controller, we do a lot of checks in the OnActionExecuting()
method.
First, we ensure we have a currentPage
. In most cases we will, but if we don't we just fallback to our pre-defined PageReference
to the Product Details Page.
Next, if we are in Edit Mode and no Entry
is found, we don't want to throw a 404. Instead, we'll just display a "No entry set" placeholder page. You'll typically only see this page if you directly view the instance of the Product Detail Page.
Lastly, we validate and create our Entry
object, and put the Entry
object into our action parameter, so we don't need to re-fetch the Entry
. As you can see, if at anytime something is missing or invalid, we throw a 404.
Business/Attributes/ValidateEntryAttribute.cs
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateEntryAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentPage = filterContext.ActionParameters["currentPage"] as ProductDetailPage;
if (currentPage == null)
{
// Update this line so it points to your Product Detail PageReference
currentPage = (new ContentReference(123)).GetPage<ProductDetailPage>();
filterContext.ActionParameters["currentPage"] = currentPage;
}
if (filterContext.ActionParameters["entryCode"] == null && filterContext.HttpContext.GetContextMode(filterContext.RouteData) == ContextMode.Edit)
{
filterContext.Controller.ViewData.Model = PageViewModel.Create(currentPage);
filterContext.Result = new ViewResult
{
ViewName = string.Format("~/Views/Pages/{0}/NoEntry.cshtml", currentPage.GetOriginalType().Name),
ViewData = filterContext.Controller.ViewData,
TempData = filterContext.Controller.TempData
};
return;
}
if (filterContext.ActionParameters["entryCode"] == null)
{
throw new HttpException(404, "No entry found.");
}
var entryCode = filterContext.ActionParameters["entryCode"].ToString();
if (string.IsNullOrEmpty(entryCode))
{
throw new HttpException(404, "No entry found.");
}
// Update this line with your favorite method to retrieve the Entry
Entry entry = null;
if (entry == null)
{
throw new HttpException(404, "No entry found.");
}
filterContext.ActionParameters["entry"] = entry;
}
}
Using the attribute
It's actually pretty simple to use this attribute. Since we are allowing the attribute to target classes or methods, we can just decorate our ProductDetailPageController
class. Also, since we are adding the actual Entry
value to our action parameters, we can add the Entry
as a parameter in our actions. This is also why we bind the {entry}
route data to the entryCode
parameter.
[ValidateEntry]
public class ProductDetailPageController : PageController<ProductDetailPage>
{
public ActionResult Index(ProductDetailPage currentPage, [Bind(Prefix = "entry")] string entryCode, Entry entry)
{
return View();
}
}