Calling a server from JavaScript is a fundamental part of AJAX applications.
Using WebServices with SOAP and WSDL is easy if proxy objects andmethods are available in the browser.
Introduction
From the languages and programming environments like C, the .NET CLR
and Java we are know proxy generation mechanisms based on IDL and RPC
for a long time. These generated classes and files enable the programmer to
call a server-side method by calling a local method with the same name. The
implementation of the network transfer is taken off your application code.
If you want to implement a communication from JavaScript to WebServices
using SOAP it is very important to use an approach that needs only a small
amount of code. Complex and long scripts tend to be buggy.
This Proxy generator can be used on its own but is also part of the
AJAXEngine framework.
Some AJAX implementations use their own way to transport the information
between the client and the server. This implementation uses the standard
SOAP protocol and works on Internet Explorer and the Firefox browser.
How it works - in short
WebServices can be described by using the formal description standard for
WebServices called WSDL (WebService Description Language). Everything
we need to know for calling a WebService is available in this XML formatted
information.
Based on this service description it is possible to generate JavaScript source
code that contains methods and some descriptive information. Because WSDL
is formatted in XML the transformation can be done by using the XSLT
technology.
The transformation itself is really simple. The real implementation is inside
the wsdl.xslt file.
Using the proxy
To make these proxy functions work a common JavaScript include (ajax.js)
file and a file that generates the WebService specific code must included.
<script type="text/javascript" src="ajax.js"></script>
<script type="text/javascript" src="getjavascriptproxy.aspx?
service=../S02_AJAXCoreSamples/CalcService.asmx"></script>
The implementation of the real communication details are implemented in the
ajax.js file. A variable named “proxies” is created as an empty JavaScript
Object and this is the only one global variable that we need. The individual
proxies are then attached to this Object to minimize the naming conflicts that
may occur.
The second script include now retrieves the WSDL description of the
WebService and generates the specific JavaScript for this service containing
local proxy methods that just can be called to execute the corresponding
method on the server.
Asynchronous calls
Calling a server-side method may look like this:
// hook up a method that gets the response
proxies.CalcService.CalcPrimeFactors.func = displayFactors;
// now call the server
proxies.CalcService.CalcPrimeFactors(12);
// The return value is passed to this function as a parameter
function displayFactors (retVal) {
document.getElementById("outputField").value = retVal;
} // displayFactors
Here you see an asynchronous call. The function CalcPrimeFactors() returns
immediately and the client side scripting continues. After some milliseconds
(or longer) the server will send back the result of the called method of the
WebService and the value will be passed to the hooked up method as a
parameter.
Synchronous calls
There is also a synchronous version that can be used. In this case the func
attribute must remain unset or null and the result of the server-side method is
directly returned from the client side method call. This kind of calling the
server may block for some milliseconds because no user-events like typing or
clicking are processed during the call.
proxies.CalcService.func = null; // no hook up function !
// call the server and return the result.
var f = proxies.CalcService.CalcPrimeFactors(12);
Implementation details
Here is a sample extract of the code that is generated for the client to shows
how the mechanism works.
The include file ajax.js generates the global object named “ajax”:
var proxies = new Object();
Per WebService an object named like the WebService is attached to the ajax
object to hold the service specific information like the url and the namespace
of the WebService:
// JavaScript proxy for webservices
// A WebService for the calculation of prime factors.
proxies.CalcService = {
url: "http://localhost:1049/CalcFactors/CalcService.asmx",
ns: "http://www.mathertel.de/CalcFactorsService/"
} // proxies.CalcService
For each WebService method a function on the client is created that mirrors
the method on the server. The information we need to build up the full SOAP
message is attached to the function object as attributes.
// Add 2 numbers.
proxies.CalcService.AddInteger = function () {
return(proxies.callSoap(arguments)); }
proxies.CalcService.AddInteger.fname = "AddInteger";
proxies.CalcService.AddInteger.service = proxies.CalcService;
proxies.CalcService.AddInteger.action = "http://www.mathertel.de/CalcFactors/AddInteger";
proxies.CalcService.AddInteger.params = ["number1:int","number2:int"];
proxies.CalcService.AddInteger.rtype = ["AddIntegerResult:int"];
Simple Caching
The proxy implementation also offers a client-side caching feature. An
approach that leads to less traffic on the net because repeating the same calls
can be prevented.
The Http caching features, instrumented by using HTTP headers do not help
in these situations because the request is not an http-get request and there is
always a payload in the http body. Caching must therefore be realized by
some scripting on the client.
The caching feature in the JavaScript WebService proxy implementation can
be enabled by calling the method proxies.EnableCache and passing the
function that should further use caching. There is a button in the
CalcFactorsAJAX.htm sample to show how to enable this:
proxies.EnableCache(proxies.CalcService.CalcPrimeFactors)
By calling this method a JavaScript object is added that stores all results and
is used to prevent a call to the server if an entry for the parameter already
exists inside this object. This is not a perfect solution, but it works under the
following circumstances:
• The parameter must be a string or number that can be used for indexing
the properties of a JavaScript object.
• The cache doesn't clear itself. It can be cleared by calling EnableCache
once again.
• Only methods with a single parameter are supported.
Analysing problems
With proxies.service.function.corefunc is an entry point in the core
implementation of the proxies object available that may be helpful when
analyzing problems.
To set up a debug output in an alert box that displays the response of a
WebService call use:
proxies.CalcService.CalcPrimeFactors.corefunc = proxies.alertResult;
If the full response text of the received SOAP message is needed you can use:
proxies.CalcService.CalcPrimeFactors.corefunc = proxies.alertResponseText;
Instead of attaching a special function for the further processing of the
response of a WebService Call it is possible to just hook up the window.alert
function to display the result.
proxies.CalcService.CalcPrimeFactors.func = window.alert;
I recommend always attaching a function for handling possible exceptions, at
least while developing. When calling a WebService asynchronously there will
be no executing code that can catch an exception so you must provide a
method in this case.
proxies.CalcService.CalcPrimeFactors.onException = proxies.alertException;
Good tools for analyzing network problems
If there are still problems with the communication of the SOAP messages I
recommend using an http monitor tool like Fiddler:
http://www.fiddlertool.com
If you prefer to implement by using the Firefox browser I recommend the
debugger named “firebug” that has a good network tracing functionality built
in. There you can see most of the communication details and it has a fantastic
feature for analyzing the timing situations when the page gets loaded. You can
install it from
http://www.getfirebug.com/
You can find the source of the
wsdl.xslt file in the listings at the end
of this book or online.
Generating JavaScript Proxies in ASP.NET
Retrieving a WSDL description is very easy when implementing in ASP.NET.
The URL of the WebService can be used with an attached WSDL Parameter:
http://www.mathertel.de/AJAXEngine/S02_AJAXCoreSamples/CalcService.asmx?WSDL
The proxy generator can retrieve this XML document by using an
HttpWebRequest. By using a XSLT transformation it is now very simple way
to implementing a WSDL to JavaScript compiler.
// GetJavaScriptProxy.aspx
private string CreateClientProxies (string url) {
if ((url != null) && (url.StartsWith("~/")))
url = Request.ApplicationPath + url.Substring(1);
if (url.EndsWith(".asmx", StringComparison.InvariantCultureIgnoreCase))
url = url + "?WSDL";
Uri uri = new Uri(Request.Url, url);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Credentials = CredentialCache.DefaultCredentials;
// req.Proxy = WebRequest.DefaultWebProxy; // running on the same server !
req.Timeout = 6 * 1000; // 6 seconds
WebResponse res = req.GetResponse();
XmlReader data = XmlReader.Create(res.GetResponseStream());
XslCompiledTransform xsl = new XslCompiledTransform();
xsl.Load(Server.MapPath("~/ajaxcore/wsdl.xslt"));
System.IO.StringWriter sOut = new System.IO.StringWriter();
xsl.Transform(data, null, sOut);
return (sOut.ToString());
} // CreateClientProxies
The complex part lies in writing the right transformations. Inside the
wsdl.xslt file you can find the templates of the JavaScript code that define
these proxy objects. Instead of generating another XML document this
transformation produces plain text that is valid JavaScript code.
The complete GetJavaScriptProxy.aspx source code
http://www.mathertel.de/AjaxEngine/ViewSrc.aspx?file=ajaxcore/GetJavaScriptProxy.aspx
wsdl.xslt source code
http://www.mathertel.de/AjaxEngine/ViewSrc.aspx?file=ajaxcore/wsdl.xslt
JavaScript Proxy sample code
You can see the actual generated JavaScript proxy by loading the url:
http://www.mathertel.de/AjaxEngine/ajaxcore/GetJavaScriptProxy.aspx?service=../S02_AJAXCoreSam
ples/CalcService.asmx&html=true
Generating JavaScript Proxies in JAVA
This AJAX Engine was originating implemented for the ASP.NET 2.0
Platform. When porting the code to Java the only real re-implementation
needs to be done in GetJavaScriptProxy:
Here is the core part of the code:
String url = request.getScheme() + "://" + request.getServerName()
+ ":" + request.getServerPort() + request.getContextPath();
String service = request.getParameter("service");
if ((service != null) && (!service.startsWith("/")))
url += "/";
url += service;
ServletContext ctx = pageContext.getServletContext();
String xsdlFile = ctx.getRealPath("ajaxcore/wsdl.xslt");
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource(xsdlFile));
transformer.transform(new StreamSource(url), new StreamResult(out));
You can see that most of the work is done inside the XSLT transformation
using wsdl.xslt and wsdl.xslt can be used without changes.
Proxy Generator supported datatypes
Simple datatypes
Up to now only those methods where supported that where converting of the
parameters and result values was not necessary. This applies to strings and
numbers.
With this version the datatypes defined on the server and the WSDL are
passed to the client so that the datatypes can be converted using JavaScript at
runtime. In the generated proxy code, the listing of the names of the
parameters is now extended by an optional specification of the datatype.
Without this the values are treated as strings.
In the HTML object model, the JavaScript datatypes are not well supported.
The value that is displayed inside an HTML input field is always a string,
even if it’s containing only digits. So when calling the proxy functions all the
parameters are also accepted as JavaScript strings and converted (if possible)
to the right types.
XML data
Passing XML documents was implemented to make it possible to pass
complex data. In the supported browser clients the XMLDocument Object
from Microsoft or Firefox and on the server the .NET XmlDocument class can
be used.
A method has to be is declared in C# like this:
[WebMethod()]
public XmlDocument Calc(XmlDocument xDoc) {
...
return (xDoc);
} // Calc
The proxy functions also accept the XML document as a string type. In this
case, the contents of the passed string is passed directly to the server any must
for this reason contain a valid XML document without the declarations any
without any "XML processing Instructions" like <? ... ?>.
With this datatype it is possible to pass complex data directly to the server an
there is no need to define a method with many parameters if using this
datatype. If the data scheme is extended with new fields it will not be
necessary to give a new signature to the WebService.
The disadvantage of this approach is that the content of the XML document
cannot be validated by the WebService infrastructure because there is no
schema for this part of the conversation available.
Tags:
JavaScript