ARI - Asynchronous Remote Interface (Ajax)
At a Glance
|
What is ARI?
|
A .NET Ajax library for Microsoft .NET websites.
|
|
Current Version
|
1.0
|
|
Requirements
|
IIS 6.0 or later; ASP.NET 2.0 or later;
|
|
Usage
|
ASP.NET Websites
|
Quick Links: Download | Example Code | Donate
Overview
To be clear, ARI is a .NET Ajax library. But it is way different than most of the
other libraries available. It is super easy to use, but oh so powerful. Don't be
fooled by the simple interface. There is a freakin lot going on underneath the hood.
ARI is the coolest and most useful and time-saving component I have developed.
How ARI Works
ARI implements the IHttpHandler interface, called the ServiceBroker. It is the gateway
between your web page and the web server. In order to route calls to the ServiceBroker,
you need to add the following code snippet to your web.config file. It must be within
the system.web tags. This tells the IIS server that any requests made to ARIRemoteServices.axd
should go to the ARI ServiceBroker.
Add this to your web.config
The only required configuration for you to do is to add a "whitelist" of methods
that you want to allow to be called from the web browser. The previous version of
ARI used to use .Net reflection to enumerate all the methods in the App_Code directory,
but I got enough requests to change that since it there was no way to limit what
could be called from the browser. The current version requires an explicit white
list in the AsyncRemoteInterface.dll.config file. I felt adding a method list here
was much cleaner than having to clutter up the actual source code with a combination
of using statements and method attibutes. In any event, when ARI is first loaded,
this list of methods is read and cached, and remains cached until IIS or the web
site is recycled. Below is the actual method whitelist from the config file that
comes as part of the example web site when you download ARI. [More on the configuration
file later.]
The config file that comes as part of the example web site
The next thing to do is to add the following line to the top of any web page you
want to make Ajax calls from.
Add this in the head section of your web page
Because of the line you added in the web.config file, this call is routed to the
ARI ServiceBroker. The ServiceBroker sees that this call is from a web page, and
responds with two critical items. First is the JavaScript client code, which is
what allows you to make the Ajax calls in the first place. This code is always the
same. (More on this code in a second.) The second thing that gets returned is a
set of dynamically generated JavaScript methods that enable you to interact with
your server code so easily. For every distinct class and method, a JavaScript object
and method is created. For overloaded methods in the same C# class, just one JavaScript
method is sufficient. Because of this dynamic code, you can call your C# methods
from JavaScript using the following syntax. The first example call shows a call
that does not take any parameters, while the second call shows one that does take
parameters.
Example 1
Example 2
How great is that! In the first example, we are calling a C# method called GetSalesTotals,
that is a member of a class called Reporting. The first argument ( cb_GetSalesTotals
) to the method is always the callback function that will execute if the
call was successful. The second argument ( err_GetSalesTotals ) to the method
is the callback function that will be called if there was a problem. This includes
exceptions, including those in your server code that you did not have a catch block
for. ARI is already picking up your slack! :-)
Note - as a matter of convention, I tend to name my callback functions the same
as the server method I am calling, with a prefix of cb_ for the success callback,
and err_ for the error callback. It is good practice and keeps the code readable.
Here is where it gets interesting. To start with, ARI implements the complete XmlRpc
wire protocol. You would never know it though because that happens in the middle-tier,
inside the communications layer. ARI's job is to make your job easier and hide all
that complexity from you. Nevertheless, ARI serializes the request as well as the
response back for smooth and reliable network communication. At a high level, here
is what takes place during an ARI remote Ajax call, using Example 2 above to illustrate:
The dynamic code that was generated and sent to the page invokes the ARI JavaScript
client proxy, which hands the request to the XmlRpc serializer.
The data types of any parameters are determined and written to an Xml string according
to the XmlRpx specification.
Any string data is base 64 encoded before being added to the outbound Xml. This
allows transmission of otherwise illegal characters in Xml, such as greater than,
less than, etc.
An ARI internal callback that lives in the JavaScript client is substituted for
the callbacks supplied as arguments.
Using a POST method, the asynchronous call is made to the ARI ServiceBroker, sending
the serialized data as an Xml string.
The ServiceBroker receives the call, and hands it off to the ServerAgent. The ServerAgent
orchestrates the entire server side operation.
The ServerAgent hands the request to an XmlRpc deserializer. The deserializer builds
a C# object that includes necessary information such as class name, method name,
and the array of arguments. The XmlRpc deserializer also converts the inbound parameters
to their native corresponding C# types. These types were indicated in the serialized
message. See the XmlRpc spec
for complete information. Also note that incoming string data types are base 64
decoded so they can live again in the real world.
Once fully deserialized, the request object is handed off to the Executive. This
class is reponsible for the execution of the intended server side method. Using
the Microsoft reflection API, a call to the class and method having the name specified
in the request is invoked. The reflection API determines the correct match in the
event of method overloading. You should be aware that you may receive an exception
indicating that the method does not exist. That isn't exactly the case. The problem
is that a method with the implied signature does not exist. Let's say you have a
method that takes an integer as its one and only argument. Now let's say you invoked
the method from JavaScript and passed in 22.78 as the argument. If ARI were
to actually make the call to the C# method, narrowing coercion takes place and the
fraction portion of the argument would be lost. Of course, this is not acceptable,
so an exception is thrown. Conversely, if the C# method took a float or a double
as its one and only parameter, passing an integer would be ok. Widening coercion
would take place, thus promoting the integer value to the necessary float or double
with no resultant data loss.
If the method has a return type, it is received by the Executive as a result of
invoking the method. A response object is created, the result object is placed inside
it, and the response object is then handed off to the XmlRpc serializer. Although
I will discuss it later, return types are limited to types inherent to the C# language.
This should be more than sufficient for just about all your needs. But, ss I said,
more on that later. One of the greatest things about the ARI serializers and deserializers,
both on the server side in C#, as well as in the JavaScript client, is that they
can handle an infinite level of data nesting. For example, a structure can contain
other structures, arrays, basic data types, lists, and more. And each of those can
contain even more objects, and so on, and so on. For example, let's consider a server
side method that returns a DataTable. You can look at a DataTable as an array of
structures, right? Each row is a structure, with column names as the name, and the
data as the value. The parent array contains all of these structures (rows).
Once serialized, the final Xml is sent back to the ServerAgent, who, in turn, passes
it back to the ServiceBroker, where it all began. Up the chain and back down the
chain. The ServiceBroker then passes this serialized Xml string back as the response
to the original client call.
The response is received by the ARI JavaScript client callback, where it is then
handed to the ARI JavaScript deserializer. At this point ARI decides how to map
the data type(s) contained in the response message. Although there is only one (or
none if the server method has a return type of void) result object, it may contain
many objects within itself, whose data type mapping have to be determined and the
proper JavaScript data types created.
Almost there. Once the equivalent JavaScript return object is created by the deserializer,
it is passed back to the ARI client proxy. If the result object is the result of
a successful remote call, the client proxy calls the users success callback, and
passes back the result object as an argument. If however an error occurred, the
result object is a string describing the error. These errors can occur as a result
of a problem in the server method, or in ARI itself, as would be the case if you
called a server method with incorrect or missing parameters.
Important Note:
A good programmer is someone who looks both ways before crossing a one-way street.
The JavaScript user callback itself is invoked by the ARI client proxy inside a
try-catch block. This is important because an error in the user callback will unwind
the stack and cause an error inside ARI, even though the problem is the user callback
function code. To protect against this, ARI invokes the user callback code from
within a try-catch block. If the user code throws an exception, the response from
ARI will be something like Your success callback threw the following exception: bla
bla bla, where bla bla bla is the description text of the JavaScript exception.
(Bla bla bla is a very technical term you know!)
At that point, the call is complete, and the user has the result of the C# server
side method. Life is good!
Data Mapping
Table 1: Sending arguments from a Javascript client to a C# server method.
The column on the left is the datatype of the C# method parameter, and the column
on the right is a list of acceptable JavaScript datatypes that can be sent.
|
C# Datatype
|
Javascript Datatype
|
|
string
|
string
|
|
bool
|
bool (true | false)
|
|
int
|
int (no decimal places)
|
|
double, float
|
number with decimal places, int
|
|
Array (descendants of IList)
|
Array
|
|
Hashtable (descendants of IDictionary)
|
Object
|
|
DataTable
|
Array of Objects
|
|
|
Table 2: Receiving return values from a C# server method when using a Javascript
client.
The column on the left is the return datatype of the C# method, and the column on
the right is the Javacript datatype that will be received in the success callback
function. (The failure callback function always receives a string datatype.)
|
C# Datatype
|
Javacript Datatype
|
|
string
|
string
|
|
bool
|
bool
|
|
int
|
int
|
|
double, float
|
number with decimal places
|
|
Array (descendants of IList)
|
Array
|
|
Hashtable (descendants of IDictionary)
|
Object
|
|
DataTable
|
Array of Objects
|
|
|
ARI Configuration
Except for adding the methods you want to allow to be called from the browser, there
is not a lot to configure with ARI. The only option in the AsyncRemoteInterface.dll.config
is called packing and controls whether or not the dynamically generated JavaScript
methods that are sent to the browser are packed or not. Internally, the generator
uses the Dean Edwards JavaScript
packing algorithm. The two options for this configuration setting are enabled,
which is the default, or disabled.
You can download the library here and examine the code.
There are a bunch of examples on the ARI example code
page that will help you understand even further. Like I said, ARI is a huge time
saver! I just cannot imagine how long some tasks would take or the compromises I
would have to make if it weren't for this component. If you are interested in .NET
Ajax programming, or have an actual project you need to get done, do yourself a
favor and at least check it out.