Egil Hansen's dot com

Software engineering, web design, Drupal, .net

Simple Ping/Reply Service for Unit Testing AJAX/XHR requests

To make it easier for me to unit test a OData JavaScript library I am working, I created a small HTTP handler that will reply back to a AJAX request with the headers, HTTP verb and query string it received. My initial thought was to do this through a WCF service, and while this is certainly possible, I found it much easier just to create a simple HTTP handler.

using System.IO;
  using System.Linq;
  using System.Runtime.Serialization.Json;
  using System.Text;
  using System.Web;
  
  namespace ODataJS.services
  {
      public class AjaxPingHandler : IHttpHandler
      {
          public void ProcessRequest(HttpContext context)
          {
              var request = context.Request;
              var response = context.Response;
  
              // serialize PingReply object
              string jsonText = string.Empty;
              using (var ms = new MemoryStream())
              {
                  var jsonSerializer = new DataContractJsonSerializer(typeof(PingReply));
                  jsonSerializer.WriteObject(ms, new PingReply(request));
                  jsonText = Encoding.Default.GetString(ms.ToArray());
              }
  
              // set content type to json
              response.ContentType = request.AcceptTypes.Contains("application/json") ? "application/json" : "text/javascript";
  
              // wrap in d object to avoid script injection
              jsonText = "{\"d\":{" + jsonText.Substring(1, jsonText.Length - 1) + "}";
  
              // output content
              response.Write(jsonText);
          }
  
          public bool IsReusable
          {
              // To enable pooling, return true here.
              // This keeps the handler in memory.
              get { return false; }
          }
      }
  }
  

The code for the AjaxPingHandler.cs handler.

As you can see, it uses the native .net DataContractJsonSerializer to serialize a custom class called PingReply. The ContentType return header is determined by the AcceptTypes request header. If the AcceptTypes array contains “application/json” the ContentType header is set to the same, otherwise we set the ContentType header to “text/javascript”.

Just for good measure, the result data is wrapped in a dummy object { "d" : . }, just like .net’s JSON enabled services does it. It is not because I am terrible worried about script injection attacks when doing unit tests, but it makes the service behave more like other .net JSON services, which makes things consistent.

using System.Linq;
  using System.Runtime.Serialization;
  using System.Web;
  
  namespace ODataJS.services
  {
      [DataContract]
      public class PingReply
      {
          [DataMember]
          public string HTTPVerb { get; set; }
  
          [DataMember]
          public JsonDictionary Headers { get; private set; }
  
          [DataMember]
          public JsonDictionary QueryString { get; private set; }
  
          public PingReply(HttpRequest request)
          {
              Headers = new JsonDictionary();
              QueryString = new JsonDictionary();
              foreach (var key in request.Headers.AllKeys.Where(x => x != null))
                  Headers.Add(key, request.Headers[key]);
  
              foreach (var key in request.QueryString.AllKeys.Where(x => x != null))
                  QueryString.Add(key, request.QueryString[key]);
  
              HTTPVerb = request.HttpMethod;
          }
      }
  }
  

The PingReply.cs class.

The only thing interesting about the PingReply class is that it uses the JsonDictionary class to return the headers and query string instead of the native .net Dictionary collection class. I decided to use the JsonDictionary class because it serializes to a proper JavaScript object, and not a array of one-element objects like the .net Dictionary. Carlos Figueira first published the JsonDictionary class over at the msdn forums.

using System;
  using System.Collections.Generic;
  using System.Runtime.Serialization;
  
  namespace ODataJS.services
  {
      [Serializable]
      public class JsonDictionary : ISerializable
      {
          readonly Dictionary<string, object> _dict = new Dictionary<string, object>();
          public JsonDictionary() { }
          protected JsonDictionary(SerializationInfo info, StreamingContext context)
          {
              foreach (var entry in info)
              {
                  _dict.Add(entry.Name, entry.Value);
              }
          }
  
          public void GetObjectData(SerializationInfo info, StreamingContext context)
          {
              foreach (string key in _dict.Keys)
              {
                  info.AddValue(key, _dict[key], _dict[key] == null ? typeof(object) : _dict[key].GetType());
              }
          }
  
          public void Add(string key, object value)
          {
              _dict.Add(key, value);
          }
      }
  }
  

The JsonDictionary.cs class.

To make it all work, you simply need to add the following to your web.config file:

<configuration>
  <system.web>
    <httpHandlers>
      <add verb="*" path="AjaxPing.ashx"
        type="ODataJS.services.AjaxPingHandler"/>
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers>
      <add verb="*" path="AjaxPing.ashx"
        name="AjaxUnittestService"
        type="ODataJS.services.AjaxPingHandler"/>
    </handlers>
  </system.webServer>
</configuration>

Then it is just a matter of querying AjaxPing.ashx and unit test against the reply from the service.

I hope this helps others who also want to produce better JavaScript code.

Simply way of adding a default item to a data bound DropDownList

I have always found it too difficult to add a “Choose xxxxx” item to the top of a DropDownList, when the DropDownList binds to a ObjectDataSource, SqlDataSource or similar. I almost always ended up just feeding the DropDownList its content from the code behind, even though it felt like something that should be possible directly. The control do have a AppendDataBoundItems property, but most of the time you just end up with duplicated items.

Today I discovered the simplest and most elegant solution I’ve seen to date. One line of code in the code behind file is still required, but the elegance of DataSource controls is kept. In short, once the DropDownList has bound to its data source, add the “Choose xxxxx” item to the items collection of the DropDownList. The trick is to use the Insert method instead of the Add method, since the Insert method allows you to specify where the new item should be inserted, and not just added to the end of the item list.

In this example I use the OnDataBound event to insert the new item:

<asp:DropDownList ID="ddlCustomer" runat="server"
    DataSourceID="odsCustomer" DataTextField="DisplayName"
    DataValueField="CustomerId" OnDataBound="ddlCustomer_DataBound">
</asp:DropDownList>

The code behind is just one line of code (excluding the method definition):

protected void ddlCustomer_DataBound(object sender, EventArgs e)
{
    ddlCustomer.Items.Insert(0, new ListItem("Choose a Customer", "0"));
}

System.Diagnostics + ASP.Net Web Site – remember to set compilerOptions="/d:TRACE"

Today I was looking at Ukadc.Diagnostics, an extension to the System.Diagnostics namespace, and decided to build a test website myself to get more familiar with it. After reading through Ukadc.Diagnostics Reference Example I thought it would be a homerun in the first try, but it turns out that without adding compilerOptions="/d:TRACE" to each compiler in the compilers section in the Web.config, the tracing code is never compiled into the assemblies at runtime. Needless to say, I was scratching my head since my sample console application worked as expected the first time.

My compilers section ended up looking like this (it is located in the system.codedom section):

<compilers>
  <compiler language="c#;cs;csharp" extension=".cs"
            compilerOptions="/d:TRACE"
            warningLevel="4"
            type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <providerOption name="CompilerVersion" value="v3.5"/>
    <providerOption name="WarnAsError" value="false"/>
  </compiler>
  <compiler language="vb;vbs;visualbasic;vbscript"
            compilerOptions="/d:Trace=true"
            extension=".vb"
            warningLevel="4"
            type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <providerOption name="CompilerVersion" value="v3.5"/>
    <providerOption name="OptionInfer" value="true"/>
    <providerOption name="WarnAsError" value="false"/>
  </compiler>
</compilers>

Do note that this is not an issue with ASP.Net Web Applications, since they are precompiled. For more on the subject, MSDN has a great article which certainly saved me in this instance.