ASP.NET QueryString Parse Gotcha

There are times when it’s useful to throw a token parameter on a URL and have the receiving page test for the existence of the parameter. For example:

/website/page.aspx?foo

What’s significant is whether the token exists or not. It doesn’t need to have a value.

When I tried to use this trick in an ASP.NET 2.0 based application I found an issue. I expected to be able to test something like:

if (Request.QueryString["foo"] != null)

Parameters have the form <name>=<value> and I expected “foo” to be taken as a name with no value. But “foo” wasn’t a key in the QueryString collection. “foo” was interpreted as a value and the key was null.

That’s not very useful.

And I don’t recall any other web development environment behaving that way. I quickly tested against JSP and ‘classic’ ASP. Sure enough. In JSP/ASP “foo” is interpreted as a name with a null or empty value.

So how to test for “foo” in ASP.NET? I didn’t want to iterate the whole QueryString collection. That would be wasteful. I didn’t want to depend on an index position. Depending on fixed positions is brittle and counter to the whole point of using name/value pairs.

My solution? I changed my URL to

/website/page.aspx?foo=

Trouble POSTing to a classic ASP page?

POSTing to an ASP? ASP’s Request.Form requires application/x-www-form-urlencoded.

If you’re writing a web client of some sort that is trying to push data at an ASP based page via a POST request, you need to set up your HTTP headers correctly. To get ASP to parse the request body into the Request.Form collection the content-type must be “application/x-www-form-urlencoded” and the content-length must be set.

“application/x-www-form-urlencoded” is the default value for the HTML <form> element’s enctype attribute.

Problem with the Script Element for ASP Server-Side Object-Oriented JavaScript

‘Classic’ ASP (Active Server Pages) is a very quirky environment. A case in point is the handling of server side script elements.

For an ASP project I wanted to use object-oriented JavaScript (AKA JScript) on the server and I wanted to be able to reuse the JavaScript code across multiple pages. I didn’t expect to be pushing any boundaries but I quickly found myself in deep weeds.

Two parts of what I wanted to do could be considered unusual.

First, I wanted to use JavaScript on the server.

Microsoft encouraged the use of VBScript for ASP but I prefer JavaScript’s C style syntax, I think JScript’s exception handling has advantages over VBScript’s “on error”, and I like being able to use essentially the same language on both the client and the server.

Second, I wanted to write object-oriented JavaScript.

The JavaScript language is fundamentally object based yet, incongruously, object-oriented programming in JavaScript is often considered to be esoteric.

ASP server side code can be placed either within ‘<%’ and ‘%>’ delimiters (which is called a ‘scriplet’ in other environments) or within a script element with a non-standard runat="server" attribute.

In ASP reusing code across multiple pages means including shared files and in ASP 3.0 there are two ways to include a file: use a server side include directive* or a script element with a src attribute.

Link: Microsoft documentation on “Including Files in ASP Applications”

An example server side include directive:
<!-- #include file="server/include.inc" -->

The server side include directive performs a textual include of the referenced file. The included file can contain a mix of HTML and ASP scriptlets.

Since I wanted to just pull in code only, my include file would be one large scriplet.

But scriplets aren’t supported in the global.asa file. One of my goals was to avoid code duplication so I needed a way to include code without using a scriplet.

Here’s an example of a script element that includes a file server side:
<script language="javascript" runat="server" src="server/include.js"></script>

The included file can only contain code in the specified language. There’s no scriplet. That’s good.

However using script elements created a new set of issues.

In violation of the principle of least astonishment, ASP does not execute server side script in the order of its appearance within the page. The documented order of execution is:

  1. Script elements for non-default languages.
  2. Scriptlets (i.e. <% %> blocks).
  3. Script elements for the default language.

My default language was JavaScript. I would have a non-default language only if I were mixing languages, which I wasn’t.

This order of execution isn’t the whole story. Script elements for the default language appear to always be executed after the end of the page with the exception of function definitions. There’s apparently some auto-magical fix-up being performed so that functions defined in script elements can be called from scriplets.

In his blog, Fabulous Adventures in Coding, Eric Lippert wrote:

Ideally you want the server side <SCRIPT> blocks to contain only global function definitions, and the <% %> blocks to contain only “inline” code.

Link: Fabulous Adventures in Coding: VBScript and JScript Don’t Mix, at least in ASP

The apparent function definition fix-up seems to be half-baked.

In JavaScript functions are actually objects. A function named test could be equivalently defined either as:
function test() { return "test"; }

or as:
var test = function() { return "test"; }

Except in ASP the former definition would be available to scriplets and the latter definition wouldn’t be executed until the end of the page.

A bigger problem is that the order of execution has the unfortunate effect of blowing out prototype assignments.

An example will illustrate. Assume two files: circle.js and default.asp.

circle.js defines a constructor for a Circle type and assigns some additional properties to the Circle prototype. (The Circle type is borrowed from an example in the Microsoft JScript documentation.)

default.asp includes circle.js, creates an instance of a Circle, and iterates all of the members of the Circle object.

circle.js:

// circle.js

function Circle (xPoint, yPoint, radius)
{
    this.x = xPoint; // The x component of the center of the circle.
    this.y = yPoint; // The y component of the center of the circle.
    this.r = radius; // The radius of the circle.
}
Circle.prototype.pi = Math.PI;
Circle.prototype.area = function ()
    {
        return this.pi * this.r * this.r;
    }

default.asp:

<%@ language="jscript" %>
<script language="javascript" runat="server" src="server/circle.js"></script>
<html>
<head></head>
<body>
<%
var aCircle = new Circle(5, 11, 99);
    
// iterate the members/properties of the Circle object
for (var x in aCircle)
{
    Response.Write(x + " = " + aCircle[x] + "<br>");
}
%>
</body>
</html>

The output that might normally be expected:

area = function () { return this.pi * this.r * this.r; }
pi = 3.141592653589793
x = 5
y = 11
r = 99

The output that this page will actually produce:

x = 5
y = 11
r = 99

Why? Because the prototype assignments that add the pi and area members don’t execute until after the closing </html> tag.

Being able to set the prototype property is important because the prototype is an essential part of JavaScript’s support for object inheritance.

At this point some might settle for living with the evil of code duplication. That might be viable if the global.asa does little or nothing. My global.asa wasn’t going to be simple enough for that solution.

I needed a work-around. What I came up with was to create factory functions that wrapped the constructor definitions and the prototype assignments in a function scope.

Here’s a revised circle.js:

// circle.js

function createCircle(xPoint, yPoint, radius) // Circle factory
{
    function Circle (xPoint, yPoint, radius)
    {
        this.x = xPoint;
        this.y = yPoint;
        this.r = radius;
    }
    Circle.prototype.pi = Math.PI;
    Circle.prototype.area = function ()
        {
            return this.pi * this.r * this.r;
        }
    
    return new Circle(xPoint, yPoint, radius);
}

Constructing a Circle now looks like:
var aCircle = createCircle(5, 11, 99);

If there is an inheritance chain, all of the objects could be defined within the scope of the factory function and the factory function could take a selector argument to determine which type of object to construct.

The function scope trick is not a perfect solution. One issue is that a type defined within a function scope can’t be used with the instanceof operator. But the function scope trick does give me a way to get most of the benefit of leveraging JavaScript’s object oriented capabilities despite ASP’s best efforts to confound.

* The server side include directive is often confused with real SSI. It’s not SSI. It’s ASP aping the syntax of SSI. It’s a curious choice because ASP otherwise follows a very different syntax model. Another curiosity is that the documentation states “ASP includes files before executing script commands” and then belies that statement with an example of including a file multiple times by using a server side include directive within a for loop.