This question about Missing functionality: Answered
How does one handle errors resulting from calls to CGI executables or REST handlers?
I've written a script to allow batch uploads to Foswiki (similar in intent the
uploadtotwiki
script). Essentially it's just a fancy wrapper around bin/upload.
The problem is that there is no clean way of handling errors "thrown" by Foswiki. For example, if one attempts to upload to a non-existent topic, Foswiki returns a nice web page indicating the problem, but it's just gibberish to my code. I could scrape the resulting HTML, but what do I look for?
The same problem must exist for REST operations.
Is there a standard interface for communicating errors back from REST handlers (and by extension the Foswiki executables)?
--
DiabJerius - 22 Nov 2008 - 00:37
Answer
Catching errors "thrown" by Foswiki usually involves adding a try..catch.
my $ok = 1;
try {
... do something dodgy ...
} catch Error::Simple with {
$ok = 0;
# note: you cannot return from the containing function here
} catch Foswiki::OopsException with {
$ok = 0;
};
...if !$ok, handle the error return (see below)...
See
CPAN:Error for full information on
try...catch
.
You can also catch errors using:
eval {
... do something dodgy ...
};
if ($@) {
... there was an error, but you're on your own working out what it was...
}
It depends somewhat on where the handler will be called, but there are three main tactics for handling the return once you know there has been an error:
- Generate a redirect
- Return an HTTP status code
- Generate an error page
Redirecting to an error page
Redirecting to an error page is straightforward.
if ( ! $ok ) {
my $url = Foswiki::Func::getScriptUrl(
$web, $topic, 'oops',
template => "oopssaveerr",
param1 => "Could not save because it's not Wednesday");
Foswiki::Func::redirectCgiQuery( undef, $url );
return undef;
}
In this example we re-used a standard 'oops' message, but you could substitute your own error URL there. The above example was taken from
WorkflowPlugin.
Note that if you were catching an error that was thrown was an OopsException, you can simply:
my $ok = 1;
try {
... do something dodgy ...
} catch Foswiki::OopsException with {
$ok = 0;
shift->redirect(); # This will generate the oops redirect
};
return undef unless $ok;
Returning an HTTP status code
This is typically what you want to do when the REST handler is called from Javascript. Here's how I do it in
WysiwygPlugin:
if ( ! $ok ) {
$response->header(-status => $status);
$response->body($text);
return undef;
}
(
$response
is the fourth parameter to REST handlers in Foswiki)
Generating an error page
This is as simple as returning a valid HTML page to the REST dispatcher that called your handler. The dispatcher deals with returning it to the client.
if ( ! $ok ) {
my $page = CGI::header('text/html').CGI::start_html. <<YOURPAGE .CGI::end_html;
... your error page here
YOURPAGE
return $page;
}
I don't know of any examples of this offhand.
--
CrawfordCurrie - 22 Nov 2008 - 19:21
Thanks very much for the info, but unfortunately it seems that I wasn't as clear in my question as I thought I was. Let me try again.
A client connects to a Foswiki executable (
bin/upload
,
bin/rest
) via http. It gets back either a server error or an HTML page. In the former case it's obvious how to handle it. In the latter case, the
contents of the HTML will indicate success or failure.
It seems that the client must then parse the content in order to recognize what has happened.
Is there a standard way of encoding success/failure in that page so that the client can take appropriate action? From the replies above I'm guessing the answer is
no.
--
DiabJerius - 22 Nov 2008 - 23:07
Ok, upon rereading the response once more it seems that there are two current modes of response:
- Returning an oops page (or equivalent) which is suitable for interacting with a human
- Setting the HTTP status and returning a message describing the error, which is more suitable for interacting with code
The first mode is appropriate for non-REST interaction (that is, everything
but /bin/rest
), while the second
should be the standard for interacting with
/bin/rest
. In the "REST" mode, I'm guessing that
- There is no current standard as to which HTTP status to return to indicate that the error resulted from within the rest handler. For example, if the client is trying to upload a file to a non-existent topic, a 404 error is inappropriate, as that means something else entirely. There should be a specific status code (my favorite status from HTTP::Status is RC_PAYMENT_REQUIRED) indicating that the REST call has failed. The client would then know to look in the HTML body for more information.
- There is no standard means to encode the error message in the HTML such that it is easily parsed (and thus understood by the client).
- There is no standard set of errors (akin to
errno
) to communicate the error to the client.
--
DiabJerius - 22 Nov 2008 - 23:57
"There is no current standard" - no, there is a standard:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
RC_PAYMENT_REQUIRED means something different altogether.
If you require extended status indicating exactly what failed, then you have the content of the response. But you are right that there is no standard (or even guidelines) for encoding this content. Personally I used predefined strings that are prepended to the content - for example, 'ERROR:', to pass this sort of information. This is part of the contract between the handler and the client, and as such is application-specific.
BTW if I was going to write an extended error status standard, I would probably use a different MIME type on the response e.g. text/json or even XML. But for 99% of applications it's total overkill IMHO.
--
CrawfordCurrie - 23 Nov 2008 - 11:27
I hadn't realized that the scope of the HTTP status was not limited to the web server but also included other applications.
If there is no standard for encoding error information, than perhaps Foswiki should implement its own. I'm not technically competent enough to suggest what form it should take, as that's not my area of expertise. However, as someone who is developing clients which interact with Foswiki, I would prefer a single, well documented interface to extracting that information. A mechanism in the core to produce the errors, and perhaps some form of a client SDK, would make writing robust client apps much easier.
BTW, RC_PAYMENT_REQUIRED was meant as a joke.
Thanks.
--
DiabJerius - 23 Nov 2008 - 20:16
This is inextricably linked to the cleanup of the
oops
script that we have been discussing.
BTW if you need an empty status code,
I'm a Teapot (418) is suitably neutral.
--
CrawfordCurrie - 25 Nov 2008 - 08:33