Flash Remoting: Error-proofing the Connection class
We all love Flash Remoting (and in particular AMFPHP). We ambitiously defend this new paradigm of RIA communication, emphasizing it’s numerous advantages and the significant, immediate benefits we can achieve by introducing remoting techniques into our code.
However, as Jeffery Bennett recently pointed out to me, the Connection class does not dispatch onStatus events if the gateway is inaccessable for whatever reason. This posting shows you have to add this capability with a few simple lines of ActionScript, giving you the ability to elegantly handle such errors.
This is how you would typically construct a simple method call using AMFPHP:
import mx.remoting.*;
import mx.rpc.*;
import mx.remoting.debug.NetDebug;
var gatewayUrl:String = "http://dev.caleb.org/amfphp/gateway.php"
var service:Service = new Service(gatewayUrl, null, 'HelloWorld');
var pc:PendingCall = service.say("Hello world!");
pc.responder = new RelayResponder(this, "handleResult", "handleError");
function handleResult(re:ResultEvent)
{
trace('The result is: ' + re.result);
}
function handleError(fe:FaultEvent)
{
trace('There has been an error');
}
The Connection class is typically employed by the Service class. If you inspect the signature of the Service class constructor, you’ll notice that the 5th [optional] parameter is the responder.
Service ( gatewayURI, logger, serviceName, conn, resp)
The first example doesn’t employ that handler, and even if one where to pass a responder, it wouldn’t receive error event dispatches. The Service class documentation states, “The resp parameter allows you to specify the responder object that contains the result and fault handling methods that receive control when the service returns a result or a fault condition.”
The problem is, the Service class doesn’t receive a fault condition if the Connection‘s gateway creation fails. The Service class assumes that the Connection class has a gateway that is online. Of course, there is no guarantee that this can happen; it’s obviously a potentially erroneous assumption.
In order to error-proof the call, we need to make 2 simple changes to the Connection class, and 1 simple change to the Service class (in addition to passing a valid responder in as the 5th arugment to the Service class).
Changes to the Connection class:
- Initialize w/ ASBroadcaster
- Dispatch onStatus event
Change to the Service class:
- Subscribe to the Connection class’ events
Following is the actual code we had to modify:
Connection class:
- Added 1 line to constructor
- Added onStatus method which dispatches onStatus events using ASBroadcaster
AsBroadcaster.initialize(this);
public function onStatus( status ):Void
{
trace('[Connection Class] onStatus invoked');
this.broadcastMessage('onStatus');
}
Service class:
- Added 2 lines @ Line 73
__conn.addListener(this);
__conn.addListener(__responder);
The “error-proof” example below shows you how to modify both the Service class and the Connection class (Note: I intentionally broke the following code in order to ensure that the onStatus method is invoked. If you want to see it work properly, change the “pdhp” to “php” in the gatewayUrl variable declaration):
import mx.remoting.*;
import mx.rpc.*;
import mx.remoting.debug.NetDebug;
var gatewayUrl:String = "http://dev.caleb.org/amfphp/gateway.pdhp"
var service:Service = new Service(gatewayUrl, null, 'HelloWorld', null, this);
var pc:PendingCall = service.say("Hello world!");
pc.responder = new RelayResponder(this, "handleResult", "handleError");
function handleResult(re:ResultEvent)
{
txtFeedback.text = 'The result is: ' + re.result;
}
function handleError(fe:FaultEvent)
{
trace('error');
txtFeedback.text = 'There has been an error';
}
function onStatus( status ):Void
{
trace('onStatus invoked')
}
The files are available for download here: http://dev.caleb.org/download/code/ErrorProofRemotingConnection.zip
I would love to hear any feedback anyone might have
| Print article | This entry was posted by Caleb on October 19, 2006 at 4:15 pm, and is filed under Caleb, Flash Remoting. Follow any responses to this post through RSS 2.0. Both comments and pings are currently closed. |
Comments are closed.
about 5 years ago
Very nice !! Thanks for sharing.
However i am encountering trouble while testing your example. It all works fine in your example. BUT if i change the gatewayURL to mine (amfphp 1.2) and replace your service.whatever call so it works with my setup/function, then i will get an “onStatus” call no matter if the connection succeeded or failed. It just comes up all the time. Suggestions?
about 5 years ago
One thing is that on your server the common trace message “Error opening URL “http:// xyz” is not even showing up when pointing to a nonexisting gatewayURL. On my server i am allways getting this error.
about 5 years ago
Klark, thanks for the comments. I haven’t yet encountered that situation. I’m travelling on business today, but I will try to reproduce and resolve the problem ASAP. Thanks for the feedback
about 5 years ago
Hi!
I saw your code, and i’m having a problem with Connection class that only occurs in Flash Player 8 and above.
When I publish to Player 8, my Connection.onStatus fires
to function correctly. The problem is that the status
information is undefined!
myConnection.onStatus=function(status)
{
trace(“onStatus”);
trace(status.details);
trace(status.description);
}
Any idea?
Thanks in advance,
Rui Pereira
about 5 years ago
Rui,
You are accurate in your observation that no status information is being dispatched with the onStatus method invocation. I’ll add that to my list of fixes to address when time permits … thanks for the comment
about 5 years ago
I tried your example and could not get it to fire any error. I even downloaded your files. And used those and still nothing. I modified both class files. I’m using flash 8 and publishing to 8 as well.
about 5 years ago
Hello Caleb,
Thanks a lot for sharing this code! I wouldn’t have known about this issue otherwise.
Unfortunately, I just can’t get it to work properly. Adding ‘this’ as the last parameter for the service call will return a ‘type mismatch’ compiler error, and simply removing it (or adding a valid responder param) doesn’t do anything at all (as in no RPC code gets executed). Changing to a valid gateway path does get things rolling again, although onStatus() never gets called either way.
I am using version 9.0r28 of the flash player. My code differs slightly from yours in that I’m adding a logger as a second parameter to my service call (in case this makes any difference).
Thanks for your time, and good luck on your business trip!
about 5 years ago
If you want avoid patching, you can make use of the connection argument passed to the Service class. For example:
var gatewayUrl:String = “http://dev.caleb.org/amfphp/gateway.php”
var conn:Connection = new Connection();
conn.onStatus = function( info ):Void
{
if (info == undefined) {
trace(“The host can’t be reached”);
} else {
trace(“onStatus: ” + info.code);
}
}
conn.connect(gatewayUrl);
var service:Service = new Service(null, null, ‘HelloWorld’, conn);
var pc:PendingCall = service.say(“Hello world!”);
pc.responder = new RelayResponder(this, “handleResult”, “handleError”);
function handleResult(re:ResultEvent)
{
trace(‘The result is: ‘ + re.result);
}
function handleError(fe:FaultEvent)
{
trace(‘There has been an error’);
}
Hope that helps.
about 5 years ago
Awesome code Ezequiel! I like your solution rather than patching core classes. Thanks a lot!
Damn it, it’s so easy actually.. and I was looking for such a long time for a solution!
Thanks guys.
about 4 years ago
If any one change the gatewayURL to mine (amfphp 1.2) and replace your service.whatever call so it works with my setup/function.
about 4 years ago
@Bernardo_Kuri
try to publish for Flash 7 to see if it fix the type mismatch error. If so when publishing for Flash 8 define service like this :
[code]
var service:Service = new mx.remoting.Service(gatewayUrl, null, 'HelloWorld');
[/code]
Seems AS2 in Flash8 doesn’t understand well ‘new Service’ it needs ‘new mx.remoting.Service’ for not issuing a type error… I eventually ran amok before seeing this page : http://yawoong.com/board/view.php?id=tutorial_kidari70&no=127
about 4 years ago
For anyone who was attempting to use Ezequiel’s method and wasn’t successful, modify the following line:
var service:Service = new Service(null, null, ‘HelloWorld’, conn);
to become:
var service:Service = new Service(null, null, ‘HelloWorld’, conn, this);
That seemed to do the trick for me.
about 3 years ago
Is there a similar solution in AS3 ?
Thanks
about 3 years ago
ya rite but some times its not work yaar. Give me some more info
about 2 years ago
here is my solution, inspired by the original post and Ezequiel’s addition.
(Tested in CS4, using AS2)
First create a class in a separate AS file, to extend the Connection:
import mx.remoting.Connection;
import mx.controls.Alert;
class ServiceConnection extends Connection {
function onStatus(p_status:Object):Void {
if (p_status == undefined) {
//trace(“SC: The host cannot be reached”);
var al = Alert.show(
“Sorry a communications error has occurred.\n\nPlease check your network and internet connection and then reload this page.”,
“Network Error!”
);
al.setSize(360, 200);
} else {
//trace(“onStatus: ” + p_status.code); // never runs?
}
}
}
then any number of services can be created to use the same connection:
this.serviceConnection = new ServiceConnection();
this.serviceConnection.connect(gatewayUrl);
this.myService1 = new Service(null, null, “HelloWorld”, this.serviceConnection, new RelayResponder(this, “myService1ResultHandler”, “anyServiceFault”)
);
this.myService2 = new Service(null, null, “HelloUniverse”, this.serviceConnection, new RelayResponder(this, “myService2ResultHandler”, “anyServiceFault”)
);
about 1 year ago
Thanks Caleb and thanks Ezequiel. This works great & should be added to official documentation!