The Remote Service (aka xe:jsonRPCService) is an extremely useful control in XPages because it examples client- and server-side code interaction, runs asynchronously, performs well, and is easy to use. But if you don’t handle errors well, it can fail quietly and the user may never know. In this post, I’ll show how to handle client- and server-side JavaScript errors with an RPC method.
Remote Procecure Example
As a starting point, let’s look at an example of a simple RPC method that returns the date and time and displays it for the user.
RPC Control
Here’s the XPages component source:
<xe:jsonRpcService id="jsonRpcService1" serviceName="myRPCService"> <xe:this.methods> <xe:remoteMethod name="myMethod" script="return myLibraryFunction();"> </xe:remoteMethod> </xe:this.methods> </xe:jsonRpcService>
The service name is myRPCService. The method, myMethod takes no parameters and just calls an SSJS library function named myLibraryFunction().
SSJS Library Function
Here’s the SSJS function that it calls:
function myLibraryFunction() { var d = new Date(); return d.toLocaleString(); }
Client-Side JavaScript
Here’s the code that calls the RPC method:
myRPCService.myMethod().addCallback(function (response) { alert('response: ' + response); })
It calls the method in the RPC service and attaches a callback function to wait for the response. It then displays the response to the user in an alert.
Handling Client-Side Errors
This works well and RPC functionality is awesome, but you have to be careful about how you handle errors.
Take for example, this problematic update. This code ignores the response, but tries to display an invalid value (an undeclared variable) in an alert.
myRPCService.myMethod().addCallback(function (response) { console.log('before'); alert ('Invalid variable: ' + myUndeclaredVariable); console.log('after'); })
When you run the code, it appears that nothing happens. The console shows the ‘before’ statement, but it then fails quietly.
Putting a try-catch block around it like this doesn’t help at all.
try { myRPCService.myMethod().addCallback(function (response) { console.log('before'); alert ('Invalid variable: ' + myUndeclaredVariable); console.log('after'); }) } catch (e) { alert('Error: ' + e.toString()); }
This may seem a bit confusing, but it actually makes sense. The callback effectively runs in its own scope, so the error handling needs to be within the callback in order to fire when dealing with an RPC call.
This code does the trick:
myRPCService.myMethod().addCallback(function (response) { try { console.log('before'); alert ('Invalid variable: ' + myUndeclaredVariable); console.log('after'); } catch (e) { alert('Error: ' + e.toString()); } })
Handling Server-Side Errors
If there’s an unhandled exception on the server-side code that runs (such as an error or forgetting to return a response), it also fails quietly, although it will show a few errors in the browser console.
If you expand the POST, you’ll see that it returns a stack trace for an RPC error.
In this case, error handling in the client-side callback function doesn’t help at all, so it’s important to handle the error in the server-side code and return something that the callback can deal with.
function myLibraryFunction() { try { print (myUndeclaredVariable); var d = new Date(); return d.toLocaleString(); } catch (e) { return 'Error: ' + e.toString(); } }
Now, I’m returning a more informative message to the user about what went wrong.
I generally like to set up my server-side methods to return an object to the callback function, because that way I can add as many properties as I want in order to return all of the information that I need. I generally include a property for a boolean value for whether the method was successful and another value that contains a success or error message. In the callback, those values can be checked and appropriate action can be taken accordingly.
