Html response store for Sitecore

02 January 2012
Marek Musielak
Frink_Cognifide_2016_HeaderImages_0117
Some time ago I worked on an Sitecore portal where one of the requirement was that the site must look nice on number of mobile devices. It wasn't easy to debug problems with html for all the devices, especially as there is no obvious way of accessing the html source of the pages rendered for the device. And many times it's hard to get the exact model of the phone for which the problem occurs.

We've tested the site against number of device models and we were happy with what we achieved, but one of the customer's employees said that the site looks incorrect on a Blackberry. When I asked about the model details, the only response I got was "Blackberry with OS 6" and that was it. Fortunately, I had developed the solution described below and I had deployed it on the servers before the tests. By  asking when the test was performed, I was able to find the exact model and type of the device. And guess what, we've found out that the model prepared for the French market displayed the site in a different way than the model for UK market (which we used for tests).

But lets start from the beginning. While developing the site I came up with an idea to store the responses (with some additional details about the request) on the server, so if an issue is raised, I may check what was the exact type and model of the device and what html response the application sent, knowing only the time the page was accessed. The solution is pretty simple and can be easily enabled and disabled by the single-line change in one of the configuration files. Applying the solution contains 2 steps only. First steps is registering page adapter in /App_Browsers/Form.browser:
<browsers>
  <browser refID="Default">
    <controlAdapters>
      <!--
        other adapters here
          ...
      -->
      <adapter controlType="My.Namespace.BasePage"
          adapterType="My.Namespace.ResponseStorePageAdapter, My.Assembly" />
    </controlAdapters>
  </browser>
</browsers>
I do really suggest that all your layouts inherit from one base class, so you can easily register the adapter for them. You can always use System.Web.UI.Page but it will register the adapter for authoring-tool pages as well and we don't need it.

The second step is to create the adapter class. The code below is the simplest version of the code - after the html is returned to the end user, the text of the response is saved in the file with some additional information about the request attached on the end of the file. You may want to add some additional features to this code. The enhanced version of the adapter I use in my applications uses the directory set in the web.config file and performs some html formatting before saving the html to the file. You can download it from here: Html Response Store For Sitecore.
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;

public class ResponseStorePageAdapter : System.Web.UI.Adapters.PageAdapter
{
  private const string ResponsesStorageFolder = @"c:\temp\";

  protected override void Render(HtmlTextWriter writer)
  {
    StringWriter stringWriter = new StringWriter();
    base.Render(new HtmlTextWriter(stringWriter));
    string html = stringWriter.ToString();

    // write the generated html to the response stream
    writer.Write(html);

    // save the html to the text file
    SaveToFile(html);
  }

  private void SaveToFile(string html)
  {
    // remove all the character that can not be used in file name
    string filteredUrl = Regex.Replace(
      String.Format("{0}://{1}{2}", Page.Request.Url.Scheme,
	  Page.Request.Url.DnsSafeHost, Page.Request.RawUrl),
	  "[^a-z0-9_.=&]", "-", RegexOptions.IgnoreCase);

    // prepare file path so it is easy to find the file we're looking for
    string filePath = String.Format("{0}{1}__{2}.html", ResponsesStorageFolder,
      DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss.fff"), filteredUrl);

    // save the html with some additional information about the request
    File.WriteAllText(filePath, html + GetAdditionalRequestInfo());
  }

  private string GetAdditionalRequestInfo()
  {
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("<!--");
    sb.AppendFormat("Url: {0}://{1}{2}\n", Page.Request.Url.Scheme,
      Page.Request.Url.DnsSafeHost, Page.Request.RawUrl);
    sb.AppendFormat("Referrer: {0}\n", Page.Request.UrlReferrer);
    // comment this line if you don't use Sitecore
    sb.AppendFormat("Device: {0}\n", Sitecore.Context.GetDeviceName());
    sb.AppendFormat("IP: {0}\n", Page.Request.UserHostAddress);
    sb.AppendFormat("User Agent: {0}\n", Page.Request.UserAgent);
    sb.AppendLine("-->");
    return sb.ToString();
  }
}
Any thoughts? Do not hesitate to comment below.