Cutting Edge

AJAX application architecture, Part 2

Dino Esposito

Code download available at:CuttingEdge2007_10.exe(151 KB)

Contents

Logging In with AJAX
Flavors of Services
What Do AJAX Services Return?
JSON Versus XML
Security Concerns
Services in ASP.NET AJAX
ASP.NET AJAX Services and SOAP
Invoking AJAX Services
Building the User Interface
What It All Means

When using ASP.NET AJAX Extensions to transition your site to an AJAX experience, there are two main programming models to choose from: partial rendering and script services. In last month's column, I discussed partial rendering from a primarily architectural perspective. Simply put, with partial rendering, you don't need to change the underlying architecture of an ASP.NET application—it's an easy route to implementing some of the best elements of AJAX, such as no-flicker updates to pages on your site. This enhanced behavior is achieved by simply adding a few new server controls (specifically, ScriptManager and UpdatePanel) and having them silently perform a few tricks to transform a traditional postback in an asynchronous request run through the XMLHttpRequest object. This approach is pretty easy as it simply applies AJAX functionality to the existing Web development model.

If you're ready for a full paradigm shift in building AJAX applications, then you should be looking at the script services approach. All in all, the typical AJAX architecture is fairly easy to understand. Figure 1 illustrates a high-level view of how it works. There's a back end made of application-specific services, which are usually just an AJAX script-callable façade on top of the middle tier of the system where the business logic lives and works. Services and the front end exchange data via HTTP using a variety of formats to pass parameters and return values. After receiving and processing data, the front end, which is made of JavaScript code running on the client, faces the nontrivial task of building a graphical UI using HTML and JavaScript. The reliance on JavaScript is a structural constraint of the browser and this will only change once the browser can support more powerful programming capabilities.

Figure 1 Typical AJAX Architecture

Figure 1** Typical AJAX Architecture **

Logging In with AJAX

Moving away from the classic ASP.NET model has numerous practical repercussions. Consider the login process and see how it would change in a pure AJAX solution.

Currently with ASP.NET, enabling the login process consists of configuring the login page with the Login control, configuring protected pages with the LoginView control, and setting up the ASP.NET membership provider. Protected pages will use either logged-in or anonymous templates to reflect the result of the authentication process graphically. In ASP.NET 2.0, you can do most of this work without writing any code. For a successful login to take place, though, there is a redirection (HTTP 302) when the user hits a protected page, a postback from the login page to authenticate credentials, and then another redirection back to the originally requested page.

This isn't necessarily the case with AJAX pages. If the user requests a protected page from the address bar, there's not much you can do other than repeating the ASP.NET experience. If, however, you have links in your page that point to protected pages, you can add some script to the onclick event of the link to check whether the user is authenticated. If not, you can then pop up an alert box to warn the user, like this:

function checkFirst() {
    var loggedIn = Sys.Services.AuthenticationService.get_isLoggedIn();
    if (!loggedIn) {
        alert("You must be logged in to follow this link");
        return false;
    }
    return true;
}

As you can see, this approach uses a client framework that, among other things, allows you to check whether the current user is logged in. With ASP.NET AJAX Extensions, you do that through the Sys.Services.AuthenticationService class.

The same service can be used to authenticate a user after she specifies valid credentials. In this case, there is no redirection and no postback. Everything happens in the context of the same page, as the following code snippet demonstrates:

function OnLogin() {
    Sys.Services.AuthenticationService.login($get("UserName").value, 
      $get("Password").value, false, null, null, OnLoginCompleted, OnLoginFailed);
}

Checking credentials becomes a standard script operation that uses a script service to authenticate. It's up to the developer to update the client page's UI to reflect that the user is logged in once authentication succeeds. Unless the framework you use provides an object model to wrap HTML in controls, you have to take the much longer route of Document Object Model (DOM) scripting. Note that ASP.NET AJAX Extensions does not currently provide an object model for representing chunks of markup as controls.

Flavors of Services

In AJAX, a service indicates a piece of code that is resident on the domain of the application and exposes functionality to client script code. A service used in AJAX requires some designing to create an implementation that shields the real application's back end and middle tier from direct interaction with the end user. Is such a service a WS-* Web service? Can it be a service-oriented architecture (SOA) service?

The ideal service for AJAX applications is primarily concerned with exposing data and resources to Web clients. It is reachable over HTTP and requires that clients use URLs (and optionally HTTP headers) to access data and command operations. Clients interact with the service using HTTP verbs such as GET, POST, PUT, and DELETE. In other words, the URL represents a resource and the HTTP verb describes the action you want to take on the resource. Data exchanged in those interactions is represented in simple formats, such as JSON and plain XML, as well as in syndication formats such as RSS and ATOM.

A service with these characteristics is a Representational State Transfer (REST) service. For more information on the definition of REST, you should read the original paper that describes the vision behind REST (available at ics.uci.edu/~fielding/pubs/dissertation/top.htm).

In the end, services used by AJAX applications tend not to use SOAP to communicate and are not necessarily autonomous services in the SOA sense. Instead, they are bound to the platform and the domain where they're hosted. Based on this, they can hardly be called WS-* Web services or SOA services.

In addition, these services make a point of not having public documentation or a discovery schema—unlike, for example, Web Services Description Language (WSDL) for WS-* Web services. This reduces the number of dependencies on the service and allows for more rapid evolution of the service code. All in all, the paradigm recommended for AJAX services is less ambitious than the paradigm behind Web and SOA services, but it's still effective for the context in which it is intended to be used.

Note that in the remainder of this column, I'll use the expression "AJAX services" to indicate services used to implement the back end of an AJAX application using the script services approach.

What Do AJAX Services Return?

Since AJAX services are exposed exclusively through HTTP, you can use virtually any text format to pack the body of requests and responses. JavaScript Object Notation (JSON) is the most commonly used format, but others, such as plain XML and raw text, can also be used.

JSON is a text-based format designed to move the state of an object across the tiers of an application. A JSON string can easily be evaluated to a JavaScript object through the familiar eval function. The JSON format describes the object, as shown here:

{"ID":"ALFKI", "Company":"Alfred Futterkiste"}

The string indicates an object with two properties, ID and Company, and their respective, text-serialized values. If a property is assigned a non-primitive value, say a custom object, the value is recursively serialized to JSON, like so:

"ID": "ALFKI",
"Company": "Alfreds Futterkiste",
"Location": "{"
City ":"
Berlin ", "
Country ":"
Germany "}",
}

When processed with the eval function, JSON strings become an associative array—a sort of name/value collection—where each entry has a name and a value. If the JSON string is meant to represent the state of a custom object, say Customer, it's your responsibility to ensure that the definition of the corresponding class is available on the client. In other words, the JavaScript's eval function alone extracts information out of the JSON string to a generic container. If you need such information to be exposed as a custom object, say a Customer object, the task of providing the class definition and loading data into it is entirely up to you or the framework you use. For more information on the syntax and purposes of JSON, have a look at www.json.org.

JSON Versus XML

For years, XML has been touted as the lingua franca of the Web. Now that AJAX is changing the Web as we know it, XML is being pushed to the corner in favor of JSON, as far as data representation is concerned.

JSON is slightly simpler and more appropriate for use with the JavaScript language. While you can argue over which is easier to understand for humans, JSON is certainly easier than XML for a Web browser to process. With JSON, you don't need anything like an XML parser. Everything you need in order to parse the text is built right into the JavaScript language. Because JSON is much less ambitious than XML, it is also less verbose. That's not to say JSON is perfect; the vast quantity of commas and quotes JSON requires makes it a rather quirky format.

With JSON you also gain a key architectural benefit at a relatively low cost. You reason in terms of objects everywhere. On the server, you define your entities and implement them as classes in your favorite managed language. When a service method needs to return an instance of any class, the state of the object is serialized to JSON and travels over the wire. On the client, the JSON string is received and processed and its contents are loaded into an array or a kind of mirror JavaScript object with the same interface as the server class. The interface of the class is inferred from the JSON stream. In this way, both the service and the client page code use the same logical definition of an entity.

From a purely technical standpoint, AJAX services don't strictly require JSON to be implemented as the data representation format. You could achieve the same result using XML, but then you would need an XML parser that can be used from JavaScript. Parsing some simple XML text in JavaScript may not be an issue, but using a full-blown parser is a different story. Performance and functionality-related issues will likely lead to the existence of numerous seemingly similar components that have little in common. And then there's the question of whether the JavaScript XML parser will support things like namespaces, schemas, white spaces, comments, processing instructions, and so on.

As I see it, for the sake of compatibility you would end up with a subset of XML limited to nodes and attributes. At that point, it is merely a matter of choosing between the angle brackets of XML and the curly brackets of JSON.

Security Concerns

AJAX allows for a more dynamic, interactive browsing experience. This, however, increases the surface area for common types of attacks such as cross-site scripting (XSS) and cross-site request forgery (CSRF). These types of attacks are caused by an attacker injecting script code into a Web page, generally via a URL, thereby allowing the attacker to control the Web browser— performing actions such as stealing user names and passwords or executing HTTP requests without the user's knowledge.

An attacker could, for instance, inject malicious script into the client using a dynamically created <script> tag, allowing data to then be imported into the attacker's Web site. In the case of a CSRF attack, the attacker could inject a script into the client, allowing the attacker to execute unauthorized service methods on another Web site by using saved authentication information (such as cookies) on the client.

JSON is a powerful way to pack data and deliver it to the client. However, because JSON is considered a safe subset of the JavaScript language (it excludes assignment and invocation operations), many AJAX applications simply pass JSON strings directly to the JavaScript eval function in order to create JavaScript objects. As a result, a malformed JSON string provides a new means for an attacker to execute unauthorized script on the client.

Most of these attack types rely on exploiting the GET verb on an HTTP request. Fortunately, ASP.NET AJAX Services have the GET verb disabled by default on remote services. Additionally, you can require that a special content type be set in the request: in any other case, an ASP.NET AJAX service will refuse the call. By design, any direct call made from a <script> tag to an ASP.NET AJAX service has the wrong content type and fails.

Services in ASP.NET AJAX

With ASP.NET AJAX Extensions, you can implement script services in two ways—using a special type of ASP.NET Web services and via page methods. In the former case, you just design and build a class linked to an ASMX resource:

<%@ WebService Language="C#" CodeBehind="~/App_Code/TimeService.cs" 
  Class="IntroAjax.WebServices.TimeService" %>

The class may optionally inherit from the WebService class and must be decorated with the new ScriptService attribute:

[ScriptService] public class TimeService : System.Web.Services.WebService { ... }

Each script-callable method is declared public and tagged with the usual WebMethod attribute.

Page methods are simply public, static methods—each marked as WebMethod—defined in the context of a single ASP.NET page. They can only be called from within the host page. Aside from storage differences, a call to a Web service or page method is processed in the same way by the ASP.NET AJAX environment.

You have to understand that AJAX services represent a part of your back end. They're not public Web services in the sense of a WS-* Web service, which are fully documented by a WSDL script and accessed via POST commands carrying SOAP data. AJAX services are really local services, usually defined in the same application that calls them. However, they can also be on a different Web app or even Web site, provided that they live in the same domain.

The ScriptService attribute plays a key role by enabling the ASP.NET AJAX runtime to accept calls to the service. Without the ScriptService attribute, an exception is thrown on the server when you attempt to place calls. Figure 2 shows the message that is returned when an AJAX page links to service not flagged with the attribute. Note that the page shown in the figure is never displayed to any user. You do receive all that markup when you connect to an ASP.NET AJAX page that declares to use such a service. The markup returns an HTTP 500 error code because the internal ASP.NET machinery refuses to process script calls directed at ASP.NET Web services that lack the attribute.

Figure 2 Page Referencing a Non-Scriptable Service

Figure 2** Page Referencing a Non-Scriptable Service **(Click the image for a larger view)

By default, AJAX service methods are invoked only using the POST verb and return JSON formatted data. However, by using the optional ScriptMethod attribute on individual service methods, you can change these settings. Figure 3 details the parameters supported by the ScriptMethod attribute. The data returned to the client can be changed to XML and you can even add support for GET requests. But as I mentioned, this apparently innocent change may open up new possibilities for attackers and expose the potential for cross-site calls to the method. The following code snippet shows the definition of a Web service method:

Figure 3 Parameters of the ScriptMethod Attribute

Parameter Description
ResponseFormat Specifies whether the response will be serialized as JSON or as XML. The default is JSON, but the XML format can be handy when the return value of the method is XmlDocument.
UseHttpGet Indicates whether an HTTP GET verb can be used to invoke the Web service method. For security reasons, this is set to false by default.
XmlSerializeString Indicates whether all return types, including strings, are serialized as XML. The default is false. The value of the property is ignored when the response format is set to JSON.
[WebMethod] [ScriptMethod] public DateTime GetTime() { ... }

If you're not going to change any default settings (which is what I recommend for most cases), then the ScriptMethod attribute can be omitted. The WebMethod attribute is mandatory, though.

ASP.NET AJAX Services and SOAP

Once created, an AJAX Web service is published as an ASMX resource. By default, it's a public URL and can be consumed by AJAX clients, as well as discovered and consumed by SOAP clients and tools. But you can opt to disable SOAP clients and tools altogether. Just enter the following configuration settings to the web.config file of the ASP.NET application that hosts the service:

<webServices>
  <protocols> 
    <clear /> 
  </protocols> 
</webServices>

This simple setting disables any protocols defined for ASP.NET 2.0 Web services (in particular, SOAP) and lets the service reply only to JSON requests. Note that with these settings on, you can no longer call the Web service through the browser's address bar for a quick test. Likewise, you can't ask for the WSDL adding the ?wsdl suffix to the URL.

In order to enable ASP.NET AJAX in a Web application, you must include the following settings in the web.config file:

<httpHandlers>
    <remove verb="*" path="*.asmx"/>
    <add verb="*" path="*.asmx" validate="false" 
      type="System.Web.Script.ScriptHandlerFactory, System.Web.Extensions" />
</httpHandlers>

The <remove> node drops the default HTTP handler for ASMX resources—the one handling requests via SOAP. The <add> node adds a new HTTP handler that basically checks the content type of each incoming ASMX request and serves it via JSON if the content-type header is set to application/json. Otherwise, the HTTP handler assumes the request is SOAP-based and forwards the request to the standard ASP.NET 2.0 Web service handler. The request will be rejected if the SOAP protocol is disabled.

In the end, and despite appearances, there is no SOAP necessarily involved with an ASP.NET AJAX service. But the support for SOAP clients is guaranteed, unless it is explicitly disabled in the web.config file.

For an ASP.NET AJAX service to work as expected, the incoming request must have the content-type HTTP header set to application/json. This is also an excellent remedy against cross-site attacks conducted through the <script> tag.

Invoking AJAX Services

To invoke an AJAX service, an AJAX client follows the same pattern used to reference Web services in Windows and classic ASP.NET applications. A proxy class provides the same interface locally as the remote service. In ASP.NET AJAX applications, this proxy is a JavaScript class that the runtime generates when the page is downloaded.

The JavaScript proxy class has the same name as the script service and a number of additional properties. It features the same set of methods, although with a slightly extended signature. In general, there's no need for you to snoop into the source code of the proxy class. But if you want to take a look at its structure, try invoking the following URL from the browser's address bar:

https://.../service.asmx/js

The browser will download a JavaScript file that you can save to your local disk for later perusal.

The JavaScript proxy class inherits from a base class named Sys.Net.WebServiceProxy. This provides the basic capabilities for placing JSON calls. The code download for this column provides a proxy class for a Web service with the following interface:

interface ITimeService {
    DateTime GetTime();
    string GetTimeFormat(string format);
}

The JavaScript proxy class features the properties listed in Figure 4. Each method mirrored has three parameters in addition to its regular set of arguments. They are the callback function to call if the method is successful, the callback function to call if the method fails or times out, and the context object to pass to both callbacks. The three default-related properties shown in Figure 4 allow you to reuse the same function for multiple calls—a unique JavaScript function to handle errors, for instance. Here's some sample code that invokes a remote AJAX service from an ASP.NET AJAX page:

Figure 4 Properties of the JavaScript Proxy Class

Property Description
Path Indicates the URL of the underlying Web service.
Timeout Indicates for how many milliseconds the method is allowed to run before a timeout is called.
defaultSucceededCallback Indicates the default JavaScript callback function to invoke for a successful call.
defaultFailedCallback Indicates the default JavaScript callback function, if any, to invoke for a failed or timed-out call.
defaultUserContext Indicates the default JavaScript object, if any, to be passed to success and failure callbacks.
function getTime() {
    IntroAjax.WebServices.TimeService.GetTimeFormat("ddd, dd MMMM yyyy [hh:mm:ss]", 
      onMethodComplete);
}
function onMethodComplete(results) {
    $get("Label1").innerHTML = results;
}

The callbacks invoked at the end of a method call (regardless of the result) have the following prototype:

function method(results, context, methodName)

The context parameter represents the context object specified when the call was made. The methodName parameter is a string set to the name of the service method. Finally, for a callback invoked for a successful call, the results parameter is an object that contains the JavaScript version of the method's return value. For the failure callback, instead, this represents a Sys.Net.WebServiceError object.

Building the User Interface

AJAX is all about the user's experience in the broadest sense—continuous feel, flicker-free updates, interface facilities, mashups, live data, and so on. But you can only utilize the browser and its set of programmability features—primarily, the browser's object model, DOM implementation, support for DHTML extensions, CSS, JavaScript, and plug-ins.

JavaScript is the main tool for building and manipulating the UI. The typical pattern for user interface tasks entails the client using JavaScript to invoke remote services, receiving JSON or perhaps XML data, and then rearranging the page to show changes.

Such a simple model is not necessarily effective when projected to the size and complexity of a real application. Rearranging the page to incorporate fresh data following a remote call poses non-trivial issues as the structure of the UI gets increasingly sophisticated. The point is, where's the threshold at which a sophisticated UI becomes a problem?

There are essentially three user interface features that each realistic application, especially line-of-business applications, depend upon: layout, data binding, and styling. The JavaScript environment has no support for any of them, except for some styling support through CSS. In addition, JavaScript is an interpreted language where, for example, intensive programming often results in memory issues due to subtle browser bugs and poor programming.

Consider a relatively common and simple scenario—paging a grid of data. A grid is ultimately a rather complex table that the browser has to parse and render for each request. It's hard for a user to measure the cost of this rendering operation the first time the page is served since it is incorporated in the overall page download. But once an AJAX postback request is made for a new grid page, the cost of updating the browser's window is immediately evident. The browser receives a JSON string that contains a collection of objects and must turn that collection into a new table. A big HTML string has to be built on the client, composing text in some memory buffer. The same string, if correct, then has to be rendered graphically.

When such intensive operations are performed, you may notice a less than ideal response time. For scenarios like this, above average JavaScript and DHTML skills are required in order to come up with effective solutions.

An alternative approach is to pre-generate some of the markup on the server. In this way, the remote service doesn't just return data but also incorporates markup information. On the server, the markup is built through compiled code, it can be more easily debugged and tested, and you have access to more powerful programming tools for adding accessibility features. On the other hand, more data travels over the wire (but keep in mind that the amount of data is still much less than with regular ASP.NET postbacks).

The bottom line is that more powerful tools are needed on the client in order to make creating realistic user interfaces effective from both the performance and development perspective. Good, thorough libraries of widgets and controls are essential to any developer. But even the best, most optimized libraries can't do much to counter the inherent limitation of an interpreted language, such as JavaScript. Perhaps Silverlight, a Microsoft cross-platform browser plug-in that incorporates a subset of the Windows® Presentation Foundation (WPF) framework, will eventually offer the client-side environment that Web developers will desire for their next generation of Web and AJAX applications. But Silverlight is an external plug-in; it requires a separate download, and it is still maturing. Version 1.0 has just been released, but more sophisticated features are needed in order to use it to build the presentation layer of real-world Web applications.

What It All Means

Partial rendering is the easiest approach to AJAX. It is particularly well suited for adding AJAX capabilities to legacy applications that you don't have the time, budget, or desire to redesign. From an architectural standpoint, partial rendering is a smart extension to today's ASP.NET that preserves the same application model and underlying engine.

A pure AJAX architecture is based on client and server being loosely coupled—essentially two separate worlds connected over the HTTP wire with JSON exchanging messages. In a pure AJAX architecture, you have a service-based back end and a JavaScript-powered front end. The burden of building an effective HTML UI is entirely up to you or the control library you choose. This decoupling, however, will allow for emerging technologies, such as Silverlight, to continue enabling Web developers to create more interactive user interfaces without being constrained by the server platform.

Send your questions and comments for Dinto to cutting@microsoft.com.

Dino Esposito is a mentor at Solid Quality Learning and the author of the upcoming Programming ASP.NET 3.5 Core Reference (Microsoft Press, 2007). Based in Italy, Dino is a frequent speaker at industry events worldwide. You can visit his blog at weblogs.asp.net/despos.