Tuesday, 27 February 2018

Verification of Sitecore Helix Architecture via a Fitness Function

Recently I've been reading - and recommend - the book Building Evolutionary Architectures by by Neal Ford,‎ Rebecca Parsons and Patrick Kua, where they discuss the concept of a fitness function. The idea here is that, as well as implementing the business requirements, a solution will also need to address certain non-functional concerns, such as security, performance, reliability and maintainability. Some of these can be measured or validated, and therefore tested, and ideally, tested in an automated way.

Within a Sitecore project I'm working on, we are adhering to the Helix principles that in part addresses the organisation of projects with a Visual Studio solution. You end up with many individual projects, each within a layer - foundation, feature and project.

  • The foundation layer is considered the highest level and most stable. Projects within this layer may have dependencies on each other, but not on any at the lower levels.
  • Feature layer projects are intended to be cohesive and swappable, in the sense that they can be reused across solutions with minimal impact on other projects. They can depend on foundation projects, but not any other feature ones, nor any at the lowest level.
    With a couple of caveats. In our solution, some features are themselves broken into one or more projects, which we call sub-features. It's OK and necessary that they have dependencies between them. We also of course allow test projects to reference their associated feature project.
  • The lowest level is the project layer, containing one (or more) projects representing the website(s) themselves. They can depend on feature and foundation projects, but not on each other.

Ensuring developers adhere to this and don't create inappropriate project references is an ideal job for an automated fitness function, which I've put together as a unit test using MSTest. There's a strict naming convention we follow with the projects within the solution and I'm leaning on that in order to write the test - of the form SolutionName.LayerName.ProjectName, e.g. MySolution.Foundation.Analytics.

The code for this can be found in the following gist:

Sunday, 25 February 2018

Return JSON from Sitecore rendering controller

As well as displaying the appropriate HTML and content, a Sitecore rendering controller can also be used to handle to form posts. As such you can have a single controller responsible for rendering the mark-up of a form and handling the POST request that's triggered when the form is submitted.

With this setup, I wanted to add some progressive enhancement and use the same form processing logic to handle an AJAX request. Using JavaScript we could hijack the form submission and make an AJAX request using the Fetch API. In the controller action method we can detect that an AJAX request is being made, and if so return a small JSON response, instead of the standard redirect back to the page to display a confirmation message.

This is quite a common pattern with ASP.Net MVC, making use of the Request.IsAjaxRequest() method available in the controller.

(As a quick aside, in order to use this method in an AJAX request triggered via the Fetch API, it's necessary to add a header that's used for the AJAX detection. It's also necessary to add the credentials option, to ensure cookies are passed, so we can make use of the CSRF protection offered by the ValidateAntiforgeryToken attribute).

    fetch(url,{
        method: 'POST',
        body: data,
        credentials: 'include',
        headers: {
            'X-Requested-With': 'XMLHttpRequest'
        });

When examining the response returned though, I was disappointed to discover that rather than the short JSON payload, what was actually coming back in the response was a big chunk of HTML, then the JSON, then further HTML. The reason of course was that we aren't dealing here with a standard MVC controller, rather a Sitecore one responsible for a rendering. This is executed as part of an existing page lifecycle, and so the JSON is getting output as content, within the context of a page output, as explained clearly here in Martina Welander's blog post.

To resolve this, I didn't really want to have to use a separate controller, defined with a custom route, to handle the AJAX requests - as we lose then the simplicity of having one location to post to from the client side, and one set of processing code. Instead I took the following approach:

  • On form processing, detect the AJAX request in the controller action method responsible for handling the form submission using Request.IsAjaxRequest()
  • Create an anonymous object representing the JSON response to return and serialize to a string
  • Pass that string to a custom ActionResult, which utilises HttpContext.Server.TransferRequest to end processing of the full page and pass the value to a new controller configured with a custom route
  • That controller then returns the JSON response, on it's own, with the appropriate content type

The relevant code is shown below, firstly the custom ActionResult:

    using System.Collections.Specialized;
    using System.Web.Mvc;

    public class TransferToJsonResult : ActionResult
    {
        public TransferToJsonResult(string serializedJson)
        {
            SerializedJson = serializedJson ?? string.Empty;
        }

        public string SerializedJson { get; }

        public override void ExecuteResult(ControllerContext context)
        {
            // Create a header to hold the serialized JSON value
            var headers = new NameValueCollection
                {
                    ["SerializedJson"] = SerializedJson
                };

            // And pass in the transfer request so it's available to the controller
            // that picks up generating the response.
            context.HttpContext.Server.TransferRequest("/JsonResponse", false, null, headers);
        }
    }

And then the controller that control is transferred to, in order to return the JSON response:

    using System.Web.Mvc;

    public class JsonResponseController : Controller
    {
        public ContentResult Index()
        {
            // Retrieve JSON to render from header
            var serializedJson = Request.Headers["SerializedJson"] ?? string.Empty;

            Response.ContentType = "application/json";
            return Content(serializedJson);
        }
    }

And finally an excerpt form the processing logic that handles the transfer behaviour:

    if (Request.IsAjaxRequest())
    {
       var serializedJson = JsonConvert.SerializeObject(new { success = true });
       return new TransferToJsonResult(serializedJson);
    }