Changes

Jump to navigation Jump to search
2,875 bytes added ,  10:52, 29 July 2020
This page presents one of the [[Web Application]] services. It describes how to use the RESTXQ API of BaseX.
RESTXQ, introduced by [http://www.adamretter.org.uk/ Adam Retter], is an API that facilitates the use of XQueryas a server-side processing language for the Web. RESTXQ It has been inspired by Java’sthe Java [httphttps://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services JAX-RS API]: it defines It provides a pre-defined set ofXQuery 3.0 annotations for mapping HTTP requests to XQuery functions, which in turn generate and returnHTTP responses.
Please note that BaseX provides various extensions to the original draft of the specification:
* Parameters are implicitly cast to the type of the function argument
* The [[#Paths|Path Annotation]] can contain regular expressions
* Quality factors in the [[#Content Negotiation|Accept header]] will be evaluated
* <code>%input</code> annotations, support for input-specific content-type parameters
* <code>%rest:single</code> annotation to cancel running RESTXQ functions
* Quality factors in the [[#Content Negotiation|Accept header]] will be evaluated
* Support for server-side quality factors in the [[#Content Negotiation|<code>%rest:produces</code>]] annotation
* Better support for the OPTIONS and HEAD methods
<br />
All RESTXQ [[XQuery 3.0#Annotations|annotations]] are assigned to the <code><nowiki>http://exquery.org/ns/restxq</nowiki></code> namespace, which is statically bound to the {{Code|rest}} prefix. A ''Resource Function'' is an XQuery function that has been marked up with RESTXQ annotations. When an HTTP request comes in, a resource function will be invoked that matches the constraints indicated by its annotations.
If a RESTXQ URL is requested, the {{Option|RESTXQPATH}} module directory and its sub-directories will be traversed, and all [[XQuery Extensions#Suffixes|XQuery files]] will be parsed for functions with RESTXQ annotations. Sub-directories that include an {{Code|.ignore}} file will be skipped. In addition, XQuery modules that cannot be parsed will be ignored if {{Option|RESTXQERRORS}} is enabled.
To speed up processing, the functions of the existing XQuery modules are automatically cached in main memory:
A first RESTXQ function is shown below:
<pre classsyntaxhighlight lang="brush:xquery">
module namespace page = 'http://basex.org/examples/web-page';
<title>Hello { $who }!</title>
</response>
};</presyntaxhighlight>
If the URI http://localhost:8984/hello/World is accessed, the result will be:
<pre classsyntaxhighlight lang="brush:xml">&lt;<response&gt;> &lt;<title&gt;>Hello World!&lt;</title&gt;>&lt;</response&gt;></presyntaxhighlight>
The next function demonstrates a POST request:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/form")
$agent as xs:string*
) as element(response) {
&lt;<response type='form'&gt;> &lt;<message&gt;>{ $message }&lt;</message&gt;> &lt;<user-agent&gt;>{ $agent }&lt;</user-agent&gt;> &lt;</response&gt;>
};
</presyntaxhighlight>
If you post something (e.g. using curl or the embedded form at http://localhost:8984/)...
<pre classsyntaxhighlight lang="brush:shell">
curl -i -X POST --data "message='CONTENT'" http://localhost:8984/form
</presyntaxhighlight>
...you will receive something similar to the following result:
<pre classsyntaxhighlight lang="brush:shell">
HTTP/1.1 200 OK
Content-Type: application/xml; charset=UTF-8
Content-Length: 107
Server: Jetty(8.1.11.v20130520)
</presyntaxhighlight>
<pre classsyntaxhighlight lang="brush:xml">
<response type="form">
<message>'CONTENT'</message>
<user-agent>curl/7.31.0</user-agent>
</response>
</presyntaxhighlight>
=Request=
The following example contains a path annotation with three segments and two templates. One of the function arguments is further specified with a data type, which means that the value for <code>$variable</code> will be cast to an <code>xs:integer</code> before being bound:
<pre classsyntaxhighlight lang="brush:xquery">
declare %rest:path("/a/path/{$with}/some/{$variable}")
function page:test($with, $variable as xs:integer) { ... };
</presyntaxhighlight>
<!-- TODO how matching works -->
Variables can be enhanced by regular expressions:
<pre classsyntaxhighlight lang="brush:xquery">
(: Matches all paths with "app" as first, a number as second, and "order" as third segment :)
declare %rest:path("app/{$code=[0-9]+}/order")
function page:order($full-pathcode) { ... };
(: Matches all other all paths starting with "app/" :)
declare %rest:path("app/{$path=.+}")
function page:others($path) { ... };
</presyntaxhighlight>
<!-- TODO how matching works -->
 
If multiple path candidates are found for the request, the one with more segments will be preferred.
===Content Negotiation===
Two following annotations Functions can be used restricted to restrict functions specific Media Types. The default type is {{Code|*/*}}. Multiple types can either be specified by a single or by multiple annotations. ====Consuming Data==== A function will only be taken into consideration if the HTTP {{Code|Content-Type}} header of the request matches one of the given types: <syntaxhighlight lang="xquery">declare %rest:POST("{$body}") %rest:path("/xml") %rest:consumes("application/xml") %rest:consumes("text/xml")function page:xml($body) { $body };</syntaxhighlight> ====Producing Data==== A function will only be chosen if the HTTP {{Code|Accept}} header of the request matches one of the given types: <syntaxhighlight lang="xquery">declare %rest:path("/xml") %rest:produces("application/xml", "text/xml")function page:xml() { <xml/> };</syntaxhighlight> Note that the annotations will ''not'' affect the type of the actual response: You will need to specific content supply an additional <code>[[#Output|%output:media-type]]</code> annotation or (if a single function may produce results of different types) generate an apt [[#Custom_Response|Custom Response]]. ====Quality Factors==== A client can supply quality factors to influence the server-side function selection process. If a client sends the following HTTP header with quality factors… <syntaxhighlight>Accept:*/*;q=0.5,text/html;q=1.0</syntaxhighlight>
* '''HTTP Content Types''': a function will only be invoked …and if two RESTXQ functions exist for the HTTP {{Code|Content-Type}} header of the request matches one of the given mime types. Example:<pre class="brush:xquery">%rest:consumes("application/xml", "text/xml")</pre>* '''HTTP Accept''': a function will only be invoked if the HTTP {{Code|Accept}} header of the request matches one of the defined mime types. Example:<pre class="brush:xquery">%rest:produces("application/atom+xml")</pre>addressed path with two different annotations for producing data…
By default, both mime types are {{Code|<syntaxhighlight lang="xquery">declare function %rest:produces("text/html") ......declare function %rest:produces("*/*}}") .. Quality factors supplied by a client will also be considered in the path selection process.If a client supplies the following accept header…</syntaxhighlight>
…the first of these function will be chosen, as the quality factor for <precode>*/*;q=0.5,text/html;q=1.0</precode>documents is highest.
…and if two RESTXQ functions exist with As we cannot ensure that the same {{Code|path}} annotation and client may supply quality factors, the {{Code|produces}} annotations selection process can also be controlled server-side. The <code>*/*</code> and <code>text/htmlqs</code>, respectivelyparameter can be attached server-side to the Media Type. If multiple functions are left in the selection process, the function one with the second annotation highest quality factor will be called, because the quality factor for <code>text/html</code> documents is higher than the one for arbitrary other mime types.favored:
Note that this annotation will ''not'' affect the content-type of the HTTP response. Instead, you will need to add a <codesyntaxhighlight lang="xquery">[[#Output|declare function %rest:produces("application/json;qs=1") ......declare function %outputrest:media-type]]produces("*/*;qs=0.5") ...</codesyntaxhighlight> annotation.
===HTTP Methods===
====Default Methods====
The HTTP method annotations are equivalent to all [httphttps://en.wikipedia.org/wiki/HTTP#Request_methods HTTP request methods] except TRACE and CONNECT. Zero or more methods may be used on a function; if none is specified, the function will be invoked for each method.
The following function will be called if GET or POST is used as request method:
<pre classsyntaxhighlight lang="brush:xquery">
declare %rest:GET %rest:POST %rest:path("/post")
function page:post() { "This was a GET or POST request" };
</presyntaxhighlight>
The POST and PUT annotations may optionally take a string literal in order to map the HTTP request body to a [[#Parameters|function argument]]. Once again, the target variable must be embraced by curly brackets:
<pre classsyntaxhighlight lang="brush:xquery">
declare %rest:PUT("{$body}") %rest:path("/put")
function page:put($body) { "Request body: " || $body };
</presyntaxhighlight>
====Custom Methods====
Custom HTTP methods can be specified with the {{Code|%rest:method}} annotation. An optional body variable can be supplied as second argument<syntaxhighlight lang="xquery">declare %rest:path("binary-size") %rest:method("SIZE", "{$body}")function page:patch( $body as xs:base64Binary) { "Request method: " || request:method(), "Size of body: " || bin:length($body)};</syntaxhighlight> If an OPTIONS request is received, and if no function is defined, an automatic response will be generated, which includes an <code>Allow</code> header with all supported methods.
<pre class="brush:xquery">declare %rest:method("RETRIEVE") If a HEAD request is received, and if no function page:retrieve() { "RETRIEVE was specified as request methodis defined, the corresponding GET function will be processed, but the response body will be discarded." };</pre>
==Content Types==
===Input options===
Conversion options for [[Options#JSONPARSER{{Option|JSON]]}}, [[Options#CSVPARSER{{Option|CSV]] }} and [[Options#HTMLPARSER{{Option|HTML]] }} can also be specified via annotations with the <code>input</code> prefix. The following function interprets the input as text with the CP1252 encoding and treats the first line as header:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/store.csv")
"Number of rows: " || count($csv/csv/record)
};
</presyntaxhighlight>
===Multipart Types===
A function that is capable of handling multipart types is identical to other RESTXQ functions:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/multipart")
"Number of items: " || count($data)
};
</presyntaxhighlight>
==Parameters==
The value of the ''first parameter'', if found in the [[Request_Module#Conventions|query component]], will be assigned to the variable specified as ''second parameter''. If no value is specified in the HTTP request, all additional parameters will be bound to the variable (if no additional parameter is given, an empty sequence will be bound):
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/params")
<result id="{ $id }" sum="{ sum($add) }"/>
};
</presyntaxhighlight>
===HTML Form Fields===
Form parameters are specified the same way as [[#Query Parameters|query parameters]]. Their values are the result of HTML forms submitted with the content type <code>application/x-www-form-urlencoded</code>.
<pre classsyntaxhighlight lang="brush:xquery">
%rest:form-param("parameter", "{$value}", "default")
</presyntaxhighlight>
====File Uploads====
Files can be uploaded to the server by using the content type {{Code|multipart/form-data}} (the HTML5 {{Code|multiple}} attribute enables the upload of multiple files):
<pre classsyntaxhighlight lang="brush:xml">
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="files" multiple="multiple"/>
<input type="submit"/>
</form>
</presyntaxhighlight>
The file contents are placed in a [[Map Module|map]], with the filename serving as key. The following example shows how uploaded files can be stored in a temporary directory:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:POST
)
};
</presyntaxhighlight>
===HTTP Headers===
Header parameters are specified the same way as [[#Query Parameters|query parameters]]:
<pre classsyntaxhighlight lang="brush:xquery">
%rest:header-param("User-Agent", "{$user-agent}")
%rest:header-param("Referer", "{$referer}", "none")
</presyntaxhighlight>
===Cookies===
Cookie parameters are specified the same way as [[#Query Parameters|query parameters]]:
<pre classsyntaxhighlight lang="brush:xquery">
%rest:cookie-param("username", "{$user}")
%rest:cookie-param("authentication", "{$auth}", "no_auth")
</presyntaxhighlight>
==Query Execution==
 
{{Mark|Updated with Version 9.0}}: The dubious status code {{Code|410}} (which indicates that a resource is permanently removed) was replaced with {{Code|460}}.
In many RESTXQ search scenarios, input from browser forms is processed and search results are returned. User experience can generally be made more interactive if an updated search request is triggered with each key click. However, this may lead to many expensive parallel requests, from which only the result of the last request will be relevant for the client.
With the <code>%rest:single</code> annotation, it can be enforced that only one instance of a function will be executed for the same client. If the same function will be called for the second time, the already running query will be stopped, and the HTTP error code {{Code|460}} will be returned instead:
<pre classsyntaxhighlight lang="brush:xquery">
(: If fast enough, returns the result. Otherwise, if called again, raises 460 :)
declare
}</ul>
};
</presyntaxhighlight>
By specifying a string along with the annotation, functions can be bundled together, and one request can be canceled by calling another one.
This is shown by another example, in which the first function can be interrupted by the second one. If you call both functions in separate browser tabs, you will note that the first tab will return <code>460</code>, and the second one will return <xml>stopped</xml>.
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/compute")
<xml>stopped</xml>
};
</presyntaxhighlight>
The following things should be noted:
==Custom Response==
Custom responses can be built from within generated in XQuery by returning an <code>rest:response</code> element, an <code>http:response</code> child node that matches the syntax of the [http://expath.org/spec/http-client EXPath HTTP Client Module] specification, and more optional child nodes that will be serialized as usual. A function that reacts yields a response on an unknown resource may look as follows:
<pre classsyntaxhighlight lang="brush:xquery">declare %output:method("text") %rest:path("") function page:error404() {
<rest:response>
<http:response status="404" message="I was not found.">
<http:header name="Content-Language" value="en"/>
<http:header name="Content-Type" value="text/htmlplain; charset=utf-8"/>
</http:response>
</rest:response>, "The requested resource is not available."
};
</presyntaxhighlight>
==Forwards and Redirects==
The two XML elements <code>rest:forward</code> and <code>rest:redirect</code> can be used in the context of [[Web Application]]s, precisely in the context of RESTXQ. These nodes allow e.g. multiple [[XQuery Update]]s in a row by redirecting to the RESTXQ path of updating functions. Both wrap a URL to a RESTXQ path. The wrapped URL should be properly encoded via <code>fn:encode-for-uri()</code>.===Redirects===
Note that, currentlyThe server can invite the client (e.g., these elements are not part of RESTXQ specification.the web browser) to make a second request to another URL by sending a 302 response:
<syntaxhighlight lang==="xml"><rest:forwardresponse> <http:response status="302"> <http:header name="Location" value="new-location"/> </http:response></rest:response></syntaxhighlight>
UsageThe convenience function {{Function|Web|web: wrap the location as follows<pre class="brush:xml"><rest:forward>{ $location redirect}}</rest:forward></pre>can be called to create such a response.
This results in In the XQuery context, redirects are particularly helpful if [[XQuery Update|Updates]] are performed. An updating request may send a server-side forwardingredirect to a second function that generates a success message, which as well reduces traffic among client and server. A forwarding of this kind will not change the URL seen from the client's perspective.or evaluates an updated database:
As an example, returning<pre classsyntaxhighlight lang="brush:xmlxquery"><declare %updating %rest:forward>/hellopath('/universe<app/restinit') function local:forward>create() { db:create('app', <root/pre>, 'root.xml'),would internally forward to http db://localhostoutput(web:8984redirect('/helloapp/universeok'))};
===declare %rest:redirect===path('/app/ok') function local:ok() { 'Stored documents: ' || count(db:open('app'))};</syntaxhighlight>
The function <code>[[Web Module#web:redirect|web:redirect]]</code> can be used to create a redirect response element. Alternatively, the following element can be sent:===Forwards===
<pre class="brush:xml"><rest:A server-side redirect>{ $location }</restis called forwarding. It reduces traffic among client and server, and the forwarding will not change the URL seen from the client’s perspective:redirect></pre>
It is an abbreviation for<syntaxhighlight lang="xml"><rest:forward>new-location</rest:forward></syntaxhighlight>
<pre class="brush:xml"><rest:response> <http:response status="302"> <http:header name="location" value="{ $location }"/> </http:response></rest:response></pre> The client decides whether to follow this redirection. Browsers usually will, tools like [http://curl.haxx.se/ curl] won’t unless fragment can also be created with the convenience function {{CodeFunction|-LWeb|web:forward}} is specified.
==Output==
In main modules, serialization parameters may be specified in the query prolog. These parameters will then apply to all functions in a module. In the following example, the content type of the response is overwritten with the {{Code|media-type}} parameter:
<pre classsyntaxhighlight lang="brush:xquery">
declare option output:media-type 'text/plain';
'Keep it simple, stupid'
};
</presyntaxhighlight>
===Annotations===
Global serialization parameters can be overwritten via <code>%output</code> annotations. The following example serializes XML nodes as JSON, using the [[JSON Module|JsonML]] format:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("cities")
}
};
</presyntaxhighlight>
The next function, when called, generates XHTML headers, and {{Code|text/html}} will be set as content type:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("done")
</html>
};
</presyntaxhighlight>
===Response Element===
Serialization parameters can also be specified in a REST reponse element in a query. Serialization parameters will be overwritten:
<pre classsyntaxhighlight lang="brush:xquery">
declare %rest:path("version3") function page:version3() {
<rest:response>
'Not that simple anymore'
};
</presyntaxhighlight>
=Error Handling=
==Raise Errors== If an error is raised during the evaluation of a RESTXQ function, an HTTP response with the status code 400 is generated. The response body contains the full error message and stack trace. With the {{Function|Web|web:error}} function, you can abort query evaluation and enforce a premature HTTP response with the supplied status code and response body text: <syntaxhighlight lang="xquery">declare %rest:path("/teapot")function page:teapot() { web:error(418, "I'm a pretty teapot")};</syntaxhighlight> The XQuery error code and the stack trace will be suppressed in the body of the HTTP response. ==Catch XQuery Errors==
XQuery runtime errors can be processed via ''error annotations''.
Errors may occur unexpectedly. However, they can also be triggered by a query, as demonstrated by the following example:
<pre classsyntaxhighlight lang="brush:xquery">
declare
%rest:path("/check/{$user}")
'User "' || $user || '" is unknown'
};
</presyntaxhighlight>
An XQuery error in a RESTXQ context delivers by default a HTTP status code 400 error back to the client. However, you can also define a custom error code by using the third argument of the error function: <pre class="brush:xquery">declare %rest:path("/teapot")function page:teapot() { fn:error(xs:QName('error'), "I'm a teapot", 418)};</pre> ==Catch HTTP Errors==
Errors that occur outside RESTXQ can be caught by adding {{Code|error-page}} elements with an error code and a target location to the {{Code|web.xml}} configuration file (find more details in the [http://www.eclipse.org/jetty/documentation/current/custom-error-pages.html Jetty Documentation]):
<pre classsyntaxhighlight lang="brush:xml">
<error-page>
<error-code>404</error-code>
<location>/error404</location>
</error-page>
</presyntaxhighlight>
The target location may be another RESTXQ function. The [[Request Module#request:attribute|request:attribute]] function can be used to request details on the caught error:
<pre classsyntaxhighlight lang="brush:xquery">
declare %rest:path("/error404") function page:error404() {
"URL: " || request:attribute("javax.servlet.error.request_uri") || ", " ||
"Error message: " || request:attribute("javax.servlet.error.message")
};
</presyntaxhighlight=User Authentication= If you want to provide restricted access to parts of a web applications, you will need to check permissions before returning a response to the client. The [[Permissions]] layer is a nice abstraction for defining permission checks.
=Functions=
The [[Request Module]] contains functions for accessing data related to the current HTTP request. Two modules exist for setting and retrieving server-side session data of the current user ([[Session Module]]) and all users known to the HTTP server ([[Sessions Module]]). The [[RESTXQ Module]] provides functions for requesting RESTXQ base URIs and generating a [httphttps://www.w3.org/Submission/wadl/ WADL description] of all services. Please note that the namespaces of all of these modules must be explicitly specified via module imports in the query prolog.
The following example returns the current host name:
<pre classsyntaxhighlight lang="brush:xquery">
import module namespace request = "http://exquery.org/ns/request";
'Remote host name: ' || request:remote-hostname()
};
</presyntaxhighlight>
=References=
* [http://www.adamretter.org.uk/papers/restful-xquery_january-2012.pdf RESTful XQuery, Standardised XQuery 3.0 Annotations for REST]. Paper, XMLPrague, 2012
* [http://www.adamretter.org.uk/presentations/restxq_mugl_20120308.pdf RESTXQ]. Slides, MarkLogic User Group London, 2012
* [httphttps://files.basex.org/publications/xmlprague/2013/Develop-RESTXQ-WebApps-with-BaseX.pdf Web Application Development]. Slides from XMLPrague 2013
Examples:
* Sample code combining XQuery and JavaScript: [httphttps://www.balisage.net/Proceedings/vol17/author-pkg/Galtman01/BalisageVol17-Galtman01.html Materials] and [httphttps://www.balisage.net/Proceedings/vol17/html/Galtman01/BalisageVol17-Galtman01.html paper] from Amanda Galtman, Balisage 2016.
* [[DBA]]: The Database Administration interface, bundled with the full distributions of BaseX.
=Changelog=
 
;Version 9.3
* Updated: [[#Custom Methods|Custom Methods]]: Better support for the OPTIONS and HEAD methods.
* Updated: [[#Catch XQuery Errors|XQuery Errors]]: Suppress stack trace and error code in the HTTP response.
* Removed: {{Code|rest:redirect}} element ({{Function|Web|web:redirect}} can be used instead)
 
;Version 9.2
* Updated: Ignore XQuery modules that cannot be parsed
;Version 9.0
* Added: Support for server-side quality factors in the [[#Content Negotiation|<code>%rest:produces</code>]] annotation* Updated: The dubious status Status code {{Code|410}} (which indicates that a resource is permanently removed) was replaced with {{Code|460}}* Removed: {{Code|restxq}} prefix
;Version 8.4
 
* Added: <code>%rest:single</code> annotation
;Version 8.1
 
* Added: support for input-specific content-type parameters
* Added: <code>%input</code> annotations
;Version 8.0
 
* Added: Support for regular expresssions in the [[#Paths|Path Annotation]]
* Added: Evaluation of quality factors that are supplied in the [[#Content Negotiation|Accept header]]
;Version 7.9
 * Updated: [[#Catch XQuery Errors|XQuery Errors]], extended error annotations
* Added: {{Code|%rest:method}}
;Version 7.7
 
* Added: [[#Error Handling|Error Handling]], [[#File Uploads|File Uploads]], [[#Multipart Types|Multipart Types]]
* Updated: RESTXQ function may now also be specified in main modules (suffix: {{Code|*.xq}}).
;Version 7.5
 
* Added: new XML elements {{Code|<rest:redirect/>}} and {{Code|<rest:forward/>}}
Bureaucrats, editor, reviewer, Administrators
13,550

edits

Navigation menu