Christian Grün proposed a novel approach to handling XQuery runtime errors with annotations which XQRS has decided to also support.
This involves writing error handler functions
which are there to deal with particular types of runtime errors,
you can use the %rest:error
annotation to define
the types of errors which can be handled by that function.
Consider the following example
declare
%rest:path("/age-check/{$age}")
function age-check($age as xs:integer) {
if($age < 18) then
fn:error(xs:QName("xyz:NOT-OLD-ENOUGH"), "Sorry, you're not old enough.")
else
"All is well."
};
declare
%rest:error("xyz:NOT-OLD-ENOUGH")
%rest:error-param("description", "{$description}")
function not-old-enough-caught($description as xs:string) {
$description,
send-email('admin@mycompany.org', 'an error occurred.', $description)
};
When the Error xyz:NOT-OLD-ENOUGH
is thrown by any
RESTXQ
function it will be caught by the not-old-enough-caught
function due to the %rest:error
annotation declaring
that it wishes to deal with errors matching that QName
xyz:NOT-OLD-ENOUGH
.
The Error Handler function can give the client the HTTP Response as well as doing anything else such as sending emails etc.
In the above scenario, the description of the fn:error
"Sorry, you're not old enough." was passed through to the error
handler via the %rest:error-param
annotation,
however there are several other
error parameters which can also
be passed to error handler functions.
The following example is a more elaborate example of an error handler function which takes more information about what the error was and where it occurred.
declare
%rest:error("xyz:NOT-OLD-ENOUGH")
%rest:error-param-1("code", "{$code}")
%rest:error-param-2("description", "{$description}")
%rest:error-param-3("value", "{$value}")
%rest:error-param-4("module", "{$module}")
%rest:error-param-5("line-number", "{$line}")
%rest:error-param-6("column-number", "{$column}")
%rest:error-param-7("additional", "{$additional}")
function not-old-enough-catcher(
$code as xs:QName,
$description as xs:string?,
$value as item()*,
$module as xs:string?,
$line as xs:integer?,
$column as xs:integer?,
$additional as item()*) {
do-stuff(),
give-client-response()
};
The following table shows the list of error parameters which can
be passed through to the error handler function via the use of
%rest:error-param
or in the case of multiple values,
a numbered list of error-param annotations,
e.g.
%rest:error-param-1
,
%rest:error-param-2
,
%rest:error-param-3
etc.
Variable | Type | Value |
---|---|---|
code | xs:QName | The error code |
description | xs:string? | A description of the error condition; an empty sequence if no description is available (for example, if the error function was called with one argument). |
value | item()* | Value associated with the error. For an error raised by calling the error function, this is the value of the third argument (if supplied). |
module | xs:string? | The URI (or system ID) of the module containing the expression where the error occurred, or an empty sequence if the information is not available. |
line-number | xs:integer? | The line number within the module where the error occurred, or an empty sequence if the information is not available. The value may be approximate. |
column-number | xs:integer? | The column number within the module where the error occurred, or an empty sequence if the information is not available. The value may be approximate. |
additional | document-node( element(error:error) ) |
The MarkLogic Error XML document which includes the full stack trace of the error in question. |
The %rest:error
annotation string literal can match on
err:FOAR0001
Q{http://www.w3.org/2005/xqt-errors}FOAR0001
XDMP-DIVBYZERO
Additionally to the supported formats above, the %rest:error
also supports using wildcards '*
', to match part of the
error, whether you want to only match on QName prefix,
namespace URI, local name or in
the case of using MarkLogic proprietary errors codes then
either Feature ID or Error ID.
The following function would catch all standard XQuery errors
declare
%rest:error("err:*")
%rest:error-param("code", "{$code}")
function xqt-error($code as xs:QName) {
"Oops ... a standard XQuery error occurred with the QName " || $code
};
While the following function would catch any MarkLogic proprietary Error with the Feature ID of "XDMP"
declare
%rest:error("XDMP-*")
%rest:error-param("description", "{$description}")
function marklogic-server-core-error($description as xs:string) {
"A MarkLogic XDMP Error occurred - " || $description
};
You can use the wildcard in any position as well as using it to simply catch all types of errors in one place, whatever they may be.
declare
%rest:error("*")
%rest:error-param-1("code", "{$code}")
%rest:error-param-2("description", "{$description}")
function the-mother-of-catch-alls(
$code as xs:QName,
$description as xs:string?) {
"Oooops! " || $code || " - " || $description
};
Sometimes, choosing which Error Handler function to invoke for a
given error can be ambiguous, in which case the more specific the
%rest:error
syntax the higher the preference.
The following table lists the types of syntax and the Precedence given to it.
Precedence | Syntax | Example |
---|---|---|
1 |
prefix:name Q{uri}name FEATURE-ERROR
|
err:FORG0001 Q{http://www.w3.org/2005/xqt-errors}FORG0001 XDMP-PATHRIDXNOTFOUND
|
2 |
prefix:* Q{uri}* FEATURE-* |
err:* Q{http://www.w3.org/2005/xqt-errors}* XDMP-*
|
3 |
*:name Q{*}name *-ERROR
|
*:FORG0001 Q{*}FORG0001 *-PATHRIDXNOTFOUND
|
4 |
*
|
*
|