How do you 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)?
(Question asked by
DiabJerius)
Answer
Try/Catch
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
.
eval
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...
}
Handling the error
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 it is done in
WysiwygPlugin:
if ( ! $ok ) {
$response->header(-status => $status);
$response->body($text);
return undef;
}
(
$response
is the fourth parameter to REST handlers in Foswiki)
HTTP status codes are fully described in
the Wikipedia article
If you need an empty status code,
I'm a Teapot (418) is suitably neutral.
If you require extended status indicating exactly what failed, then you have the content of the response to play with. But there is no standard (or even guidelines) for encoding this content. Because this requirement is strongly application-specific, most people use 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.
If you prefer to write Exception based code, you can replace the above code with
throw Foswiki::EngineException($status, $text) unless ($ok);
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;
}
--
CrawfordCurrie,
SvenDowideit