5
votes

I'm struggling with exception handling inside my Datasnap REST Service (Delphi XE3 but also tried with Delphi 10 Seattle). I have written half a dozen Windows Service over the years and I always include a TApplicationEvents component so that I can log any Application Exceptions to the Windows Event Log.

However, this behaviour is not happening with a Datasnap Service. The TApplicationEvents.OnException event never gets fired, so I assume something else is eating the exception and handling it before it gets here.

The exception is displayed in the web service method's result, which is fine because it means I can at least display something on the client side, but I'd also like to catch it before then so that I might be able to handle different exceptions server-side.

The only consistent way I have managed so far is to wrap each individual method in a try..except block, and handle the exception in each method, before re-raising the exception. However, with a Web Service of 20 methods and growing, this isn't really going to scale up.

I have also tried implementing the OnError, OnTrace and other events of the some of the Datasnap components (TDSServer, TDSHTTPService, TDSTCPServerTransport, etc.) but these never seem to get fired either.

Has anyone come across anything like this, please?

1
Related (for Java): How to grab uncaught exceptions in a Java servlet web application Maybe a similar solution is possible. (if there is a global request filter mechanism in Datasnap)mjn
As you noted, exceptions are being caught by the DataSnap event handler code. It's easy enough to create a template exception handler to put in each new event handler before coding the rest of the code. It's possible depending on what you are doing that you might want to respond differently in different resource requests. Of course you know all that. As an alternative, instead of creating different actions and event handlers, you could just use the default handler and parse the pathinfo yourself and just have a single event handler with an exception handler in that event.Toby

1 Answers

0
votes

Tl;Dr: it's not implemented in a usable fashion (in 10.1 Berlin).

I came across the same problem and after reading through a lot of source, I found no practical solution.

So an exemplary (my) StackTrace would look like this:

MyClass::MyServerMethod()
/* skipping some funny unimportant RTTI/TValue handling here */
System::Rtti::TRttiMethod::Invoke
Dsreflect::TDSMethod::Invoke(TObject, TDSMethodValues)
TDSServerConnectionHandler::DbxExecute(const TDBXExecuteMessage)
TDSServerCommand::DerivedExecuteUpdate
TExecuteCallback
TDSService::Execute(const string, const TRequestCommandHandler, TExecuteCallback)
TDSService::ProcessRequest(const string, const TRequestCommandHandler, TExecuteCallback)
TDSRESTService::ProcessREST(const string, const string, const TArray<Byte>, const TRequestCommandHandler)
TDSRESTService::ProcessGETRequest(const string, TStrings, TArray<Byte>, TRequestCommandHandler)
TDSRESTServer::DoDSRESTCommand(TDSHTTPRequest, TDSHTTPResponse, string)
TDSRESTServer::DoCommand(TDSHTTPContext, TDSHTTPRequest, TDSHTTPResponse)
Dshttpwebbroker::TDSRESTWebDispatcher::DispatchRequest(TObject, Web::Httpapp::TWebRequest, Web::Httpapp::TWebResponse)

Note: This depends entirely on your usage of DataSnap. In the above case requests are passed into the DataSnap API through TDSRESTWebDispatcher (comming from TIdCustomHTTPServer).

  • Every Exception raised in a ServerMethod will end up in TDSService::ProcessRequest.
  • In this procedure every Exception is caught and ONLY its Message is added to a TRequestCommandHandler->CommandList.
  • Further down the Message is written as JSON/DBX command to the output.

So we can never handle the Exception Object and access the StackTrace or other information. So this alone is unacceptable and has to change

The good news is, that this procedure is virtual and can be overwritten. The bad news is, that in the example above you would have to extend TDSRESTService with your own ProcessRequest procedure (including your errorhandler), TDSRESTServer with own DoDSRESTCommand (in there the TDSRESTService is created in a monstrously large procedure) and TDSRESTWebDispatcher (depending on your usage).

My personal recommendation is to don't use DataSnap.

Note: At the point of writing this, I haven't found any invoke of the OnError event.