URL Rewriting

(1Q23)


How to customize a web application's URLs using controller.xq scripts.

Introduction

Good web applications provide meaningful and consistent URLs to the user. eXist-db's URL Rewriting facility is eXist's oldest internal mechanism for providing short, human-readable URLs to XQuery web applications, conveniently masking the often complex hierarchy of XQuery modules, HTML files, data, and other resources. (A newer facility for achieving these goals is eXist-db's implementation of RESTXQ. A third method is to place a reverse proxy between eXist-db and the end-user. Many applications combine two or even all three of these methods.)

For a brief overview of how eXist-db's URL Rewriting facility works, consider what happens when eXist-db receives an HTTP request:

  1. eXist-db's Jetty web server receives an HTTP request. In the default configuration this is handled by the XQueryUrlRewrite servlet for any URL starting with the path /exist.

  2. The XQueryUrlRewrite servlet first checks the URL against a series of URL patterns defined in an eXist-db configuration file, called controller-config.xml and described in the section below on Base Mappings. If eXist-db finds a matching root pattern, which is /apps by default, eXist-db will look within the associated controller root path, which is the /db/apps database collection by default, for controller scripts: special XQuery files named controller.xq (or in older applications controller.xql). A controller script applies to the collection it is stored in and its descendants. They form a collection hierarchy—a URL space within which URL rewriting can be flexibly applied.

  3. The controller.xq script examines the URL using the provided Controller Variables, and produces an XML-formatted directive in the Controller XML Format.

  4. The XQueryURLRewrite servlet interprets the directive as a series of instructions on what to do next. These instructions may be as simple as forwarding the request to a resource on the server (or elsewhere), or as complex as a pipeline using the Model-View-Controller pattern (see MVC and Pipelines) and other servlets such as eXist-db's XQueryServlet or XSLTServlet. The controller script isn't limited to returning these types of instructions; like any XQuery main module, they can return any data you may wish. But URL Rewriting instructions are the special capability of controller scripts.

Example 1: Simple URL Rewriting

Consider the application you are currently using. You are likely accessing this article from the URL /exist/apps/doc/data/urlrewrite. But how is this possible? The path to the underlying document in the eXist-db database is /exist/apps/doc/data/urlrewrite.xml. If you access this URL directly, eXist-db will return the XML document's content. However, to return a properly formatted HTML page that they can easily consume in their web-browser, the author of this application wrote an XQuery to transform the XML into HTML. Let's call it transform.xq. This query can dynamically transform documentation articles into HTML, but it needs to know which article the user is interested in. The author decides to craft URLs to specific articles using a URL parameter, doc, resulting in a URL like /exist/apps/doc/modules/transform.xq?doc=urlrewrite.xml. But this URL is cumbersome. What if the author wants to expose the HTML version of documentation article through a much nicer, simplified URL like /exist/apps/doc/urlrewrite?

To achieve this goal we need a mechanism to map or rewrite a given URL to an application specific endpoint. Specifically, we need to ensure when a user visits the URL /exist/apps/doc/urlrewrite, eXist-db must initiate an XQuery process that locates the source document, transforms it into HTML, and returns the HTML page. This control is precisely what the eXist-db URL Rewriting facility provides to application creators.

The primary mechanism behind customizing URLs like this is the controller script, an XQuery main module named controller.xq. This script is invoked for all URL paths targeting the collection in which it resides. It has access to a number of Controller Variables pre-bound with details about the request, including $exist:resource, which conveniently contains the name of the requested resource (without any leading path components). It also includes $exist:controller, which contains the path to the database collection where the controller.xq is located.

This information allows a controller script to forward all requests for /exist/apps/doc/{resource} to an XQuery, transform.xq, which converts the XML document located via the {resource} path component into an HTML page. To achieve this, one would create the following controler script and store it in the database at the path: /db/apps/doc/controller.xq:

xquery version "3.1";

declare namespace exist="http://exist.sourceforge.net/NS/exist";

declare variable $exist:path external;
declare variable $exist:resource external;
declare variable $exist:controller external;
declare variable $exist:prefix external;
declare variable $exist:root external;

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <forward url="{$exist:controller}/modules/transform.xq">
    <add-parameter name="doc" value="{$exist:resource}.xml"/>
  </forward>
</dispatch>

This example controller script returns a simple <dispatch> element which will be passed back to eXist-db's URL Rewriting framework. The <forward> element instructs the framework to call the URL modules/transform.xq relative to the collection where the controller resides. It adds an HTTP Request parameter, named doc, which constructs the resource that the transform.xq module will retrieve and transform. The transform.xq module will access the doc parameter by calling the request:get-parameter() XQuery function from eXist-db's HTTP Request Module, so that it can locate the desired resource.

Example 2: Defining a Pipeline

Real world controllers are often far more complex and may contain arbitrary XQuery code to distinguish between different scenarios. The eXist-db URL Rewriting framework allows you to turn simple requests into complex processing pipelines, involving any number of steps.

For example, let us split the <dispatch> element from above into a pipeline involving two steps:

  1. Load the resource to be transformed

  2. Pass the content of the resource to modules/transform.xq

xquery version "3.1";

declare namespace exist="http://exist.sourceforge.net/NS/exist";

declare variable $exist:path external;
declare variable $exist:resource external;
declare variable $exist:controller external;
declare variable $exist:prefix external;
declare variable $exist:root external;

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <forward url="data/{$exist:resource}.xml"/>
  <view>
    <forward url="{$exist:controller}/modules/transform.xq"/>
  </view>
</dispatch>

Every <dispatch> element must contain at least one <forward> element, followed by an optional <view> element, grouping any number of follow up steps. In this example, the first <forward> element simply instructs eXist-db to load the requested XML document and then return its content. The output of the first step is passed internally via the HTTP request to the second step. The transform.xq module can access this output via the request:get-data() XQuery function from eXist-db's HTTP Request Module.

Controller XML Format

As we have seen, the controller.xq script is expected to return a single XML element: a <dispatch> or <ignore> element. The script can actually return any data, as you wish, but the <dispatch> or <ignore> elements have special properties in the context of a controller.xq script.

Note that all of the elements described in this section must be in the http://exist.sourceforge.net/NS/exist namespace.

The dispatch Action

The <dispatch> element must contain one of the action elements redirect or forward, followed by an optional view element. It may also contain an optional cache-control element.

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <redirect url="..."/>
</dispatch>

The ignore Action

The <ignore> action simply bypasses the URL Rewriting facility. This may be useful for requests to fixed resources like images or stylesheets. An alternative may be to handle requests for such resources separately from the XQueryUrlRewrite servlet; this is discussed in Locating Controller Scripts and Configuring Base Mappings.

<ignore xmlns="http://exist.sourceforge.net/NS/exist"/>

The <ignore> element may include an optional cache-control element.

The redirect Action

The <redirect> action redirects the client to another URL with an HTTP 302 redirect response, indicating that the requested resource has been temporarily moved to a new URL (supplied by the Location HTTP Header). Clients typically follow the redirect and issue a request to the new URL; users will see their web browser update and show the new URL. Note that this second request may well access the eXist-db controller again; care must be taken to avoid creaing an infinite loop.

The <redirect> element must contain a url attribute:

url

The URL to redirect the request to.

Many eXist-db applications establish redirects for requests to their root without a leading slash, to ensure that all subsequent requests to the application begin with a leading slash. This is often accomplished as the first condition in the controller.xq script:

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <redirect url="..."/>
</dispatch>

In a local copy of the eXist-db Documentation application, for example, this will cause a request for http://localhost:8080/exist/apps/doc to receive the following HTTP 302 redirect response:

HTTP/1.1 302 Found
Location: http://localhost:8080/exist/apps/doc/
Content-Length: 0

The forward Action

The <forward> action internally forwards the current request to another request path or servlet. Unlike the redirect action, the forward action is performed entirely server-side by eXist-db (via the RequestDispatcher of the servlet engine). The details of the internal forward action are not exposed to the client; i.e., the user's web browser will not show any changes to the URL.

The <forward> element must contain either a url or servlet attribute:

url

The new request path, which will be processed by the servlet engine in the normal way, as if it were directly called.

If a relative path is provided, then it is resolved relative to to the current request path. An absolute path will be resolved relative to the path that triggered the controller.

For example, if the current URL context is /exist and the supplied attribute reads url="/admin", the resulting path will be /exist/admin.

servlet

The name of a servlet as given in the <servlet-name> element of the corresponding servlet definition from eXist-db's web descriptor $EXIST_HOME/etc/webapp/WEB-INF/web.xml configuration file.

For example, valid names within the eXist-db's standard setup would be XQueryServlet or XSLTServlet. You can see some examples of these in the article MVC and Pipelines.

The <forward> element may contain the following optional attributes:

absolute

To be used in combination with url. If set to "yes", the url will be interpreted as an absolute path within the current servlet context. See Accessing Resources Not Stored in the Database for an example.

method

The HTTP method (e.g., POST, GET, PUT) to use when passing the request to the next step in the pipeline; not applicable to the first step. The default method for pipeline steps in the view section is always POST.

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <forward url="{$exist:controller}/modules/transform.xq">
    <add-parameter name="doc" value="{$exist:resource}.xml"/>
  </forward>
</dispatch>

The <forward> element can contain the optional child elements: add-parameter, set-attribute, clear-attribute, and set-header.

The view Action

The <view> action is used to define processing pipelines, and may follow redirect or forward actions.

The <view> element is used to wrap a sequence of action elements such as forward. It is often used to call another servlet to process the results of the initial action. This is discussed in the article: MVC and Pipelines

The add-parameter Option

The <add-parameter> option adds (or overwrites) a HTTP Request Parameter.

The name of the parameter is taken from the name attribute, and the value from the value attribute.

<add-parameter name="xxx" value="yyy"/>

The original HTTP request will be copied before the change is applied. This applies only to the step on which it is placed, that is to say that subsequent steps in the pipeline will not see the parameter.

To access the value of the parameter, use the request:get-parameter() XQuery function from eXist-db's HTTP Request Module.

The set-attribute Option

The <set-attribute> option adds (or overwrites) a Java Servlet request attribute. They are part of the Java Servlet specification, and are not related to the HTTP Request or HTTP Response. Request attribute are internal to the pipeline. Unlike request parameters, request attributes will be visible to subsequent steps in the processing pipeline.

The name of the request attribute is read from the name attribute, and the value from the value attribute.

<set-attribute name="xxx" value="yyy"/>

You can set arbitrary request attributes, for instance to pass information between XQuery modules. Some attribute names may be reserved by various servlets in the pipeline.

To access the value of the request attribute, use the request:get-attribute() XQuery function from eXist-db's HTTP Request Module.

The clear-attribute Option

The <clear-attribute> option clears a request attribute.

The name of the request attribute is read from the name attribute.

<clear-attribute name="xxx"/>

Since request attributes will be visible to subsequent steps in the processing pipeline, you may wish to clear them once they are no longer needed. eXist-db places no requirement on the user having to ever clear attributes.

The set-header Option

The <set-header> option sets an HTTP Response Header field.

The name of the header is read from the name attribute, and the value from the value attribute.

<set-header name="xxx" value="yyy"/>

The HTTP response is shared between all steps in the pipeline, so all following steps will be able to see the change.

The cache-control Option

The <cache-control> element is used to tell the URL Rewriting framework if the mapping used to rewrite the input URL to its dispatch rule should be cached. It has a single attribute: cache="yes|no".

Internally the URL Rewriting framework maintains a mapping between input URLs and dispatch rules. When the cache is enabled, the controller.xq script only needs to be executed once for each distinct input URL. Subsequent requests for the same URL will be served from the cache.

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <redirect url="..."/>
  <cache-control cache="yes"/>
</dispatch>

Note: Only the URL rewrite rule is cached. The HTTP response itself is not cached. The cache-control setting is completely unrelated to HTTP Cache Headers in the HTTP Response and any client-side caching within a web browser.

Controller Variables

Five URL Rewriting-related variables are pre-bound and made available to the controller.xq script for convenience. For example, if the request path is /exist/apps/sandbox/get-examples.xq these variables will be bound to the following values:

Variable Name

Variable Value

$exist:root

xmldb:exist:///db/apps

$exist:prefix

/apps

$exist:controller

/sandbox

$exist:path

/get-examples.xq

$exist:resource

get-examples.xq

These variables are pre-bound for simplicity and convenience. You do not need to explicitly declare the variables or the exist namespace. However, doing so is considered best practice, by adding this block to the prolog of your controller.xq script. For instance:

declare namespace exist="http://exist.sourceforge.net/NS/exist";

declare variable $exist:root external;
declare variable $exist:prefix external;
declare variable $exist:controller external;
declare variable $exist:path external;
declare variable $exist:resource external;

These variables can also be accessed by any query within the controller hierarchy via the request:get-attribute() function, e.g., request:get-attribute("$exist:prefix").

Some further remarks about each variable:

$exist:root

Is bound to the root of the current controller hierarchy. This may either point to the file system or to a collection in the database. You can use this variable to locate resources relative to the root of the application.

For example, assume that you want to process a request using stylesheet db2xhtml.xsl, which could either be stored in the /stylesheets directory in the root of the webapp or, if the app is running from within the database, the corresponding /stylesheets collection. You want your app to be able to run from either location. The solution is to incorporate the value of the $exist:root variable into your logic:

<forward servlet="XSLTServlet">
  <set-attribute name="xslt.stylesheet" value="{$exist:root}/stylesheets/db2xhtml.xsl"/>
</forward>
$exist:prefix

If the current controller hierarchy is mapped to a certain path prefix, then the $exist:prefix variable will be bound to that prefix.

For example: the default configuration maps the path /apps to a collection in the database (see below). In this case, the $exist:prefix variable would be bound to the value /apps.

$exist:controller

Is bound to the part of the URL leading to the current controller.xq.

For example, if the request path is /sandbox/test.xq and the controller is in the xquery collection, the $exist:controller variable will be bound to the value /sandbox.

$exist:path

Is bound to the last part of the request URL, i.e., after the section leading to the collection containing the controller.xq.

For instance, if the resource example.xml resides within the same collection as the controller query, the $exist:path variable would be bound to the value /example.xml.

$exist:resource

Is bound to the part of the URL after the last /; this is usually pointing to a resource.

For instance: example.xml.

In addition, within a controller.xq file, you also have access to the entire XQuery function library, including the functions in the HTTP request, response, and session modules.

Accessing Resources Not Stored in the Database

If your controller.xq is stored in a database collection, all relative and/or absolute URLs processed by the controller will be resolved against the database, not the file system. This can be a problem if you need to access common resources, which should be shared with other applications residing on the file system or in the database.

The forward directive accepts an optional attribute absolute="yes|no" to handle this. If one sets absolute="yes", an absolute path (starting with a /) in the url attribute will resolve relative to the current servlet context, not the controller context.

For example, to forward all requests starting with a path /libs/ to a directory within the webapp folder of eXist-db, you can build upon the following example snippet:

if (starts-with($exist:path, "/libs/"))
then
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <forward url="/{substring-after($exist:path, '/libs/')}" absolute="yes"/>
    </dispatch>

This simply removes the /libs/ prefix and sets the attribute absolute="yes", so that the path will be resolved relative to the main context of the servlet engine, typically /exist/. In your HTML, you can now write paths such as:

<script type="text/javascript" src="/libs/scripts/jquery/jquery-1.7.1.min.js"/>

This will locate the jquery file in webapp/scripts/jquery/..., even if the rest of your application is stored in the db and not on the file system.

Locating Controller Scripts and Configuring Base Mappings

By convention, the controller script must be named controller.xq (or in older applications controller.xql; if both controller.xq and controller.xql are placed in the same collection, the former takes precedence, and the latter is ignored).

If you wish to allow anonymous users to access resources using the URL Rewriting framework then you need to ensure that the controller.xq script is world-executable, e.g., sm:chmod(xs:anyURI("/path/to/controller.xq")), "o+x").

By default, the URL Rewriting framework will try to guess the path to the controller script by looking at the request path, starting with the deepest, most specific collection, and then examining each parent collection until a controller script is found or the root of the database has been reached.

This can be configured and overridden via the controller-config.xml configuration file in $EXIST_HOME/etc/webapp/WEB-INF, which defines the base mappings used.

In fact, one web application may have more than one controller hierarchy. For example, you may want to keep the main webapp within the file system, while some tools and scripts should be served from a database collection. This can be done by configuring two roots within the controller-config.xml file.

The configuration file has two components:

  • <forward> actions which map URL patterns to servlets

  • <root> elements that define the root for a file system or database collection hierarchy

The <forward> elements specify path mappings for common servlets, similar to the servlet mapping in $EXIST_HOME/etc/webapp/WEB-INF/web.xml. The advantage to specifying them in controller-config.xml is that the XQueryURLRewrite servlet (which implements the URL Rewriting framework) becomes a single point of entry for the entire web application, and no additional handling of the servlet paths is necessary in the controller script.

For example, if we had registered a servlet mapping for /rest in web.xml, we would have needed to make sure that this path is ignored in our main controller.xq scripts. However, since the mapping is done via controller-config.xml, XQueryURLRewrite handles the path, which then doesn't need to be accounted for in our controller.

The <root> elements define the roots of a directory or database collection hierarchy, mapped to a certain base path. For example, the default controller-config.xml uses two roots:

<!-- HTTP requests to /apps are mapped onto the database path /db/apps -->
<root pattern="/apps" path="xmldb:exist:///db/apps"/>

<!--
  ++ The default fallback web application is served from the
  ++ /etc/webapp directory on the filesystem. 
-->
<root pattern=".*" path="/"/>

This means that paths starting with /apps will be mapped to the collection hierarchy below /db/apps. All relative or absolute URLs within the URL space managed by the controller.xq script will be resolved against the database, not the file system. Everything else is handled by the catch all pattern pointing to the root directory of the webapp (which, by default, corresponds to $EXIST_HOME/etc/webapp). However, there's a possibility to escape this path interpretation as described below.

MVC and Pipelines

The XQueryURLRewrite servlet does more than just forward or redirect requests: the response can be processed by passing it to a pipeline of views. "Views" are again just plain Java servlets. The most common use of a view would be to post-processes the XML returned from the primary URL, either through another XQuery or an XSLT stylesheet (XSLTServlet). XQueryURLRewrite passes the HTTP response stream of the previous servlet to the HTTP request received by the next servlet. It is fully possible to extend eXist-db by adding in your custom own servlets.

Views may also directly exchange information through the use of request attributes (more on that below).

You define a view pipeline by adding a <view> element to the <dispatch> element returned by the controller. The <view> element is a wrapper around another sequence of <forward> or <rewrite> actions.

For example, assume we have some XML documents written in DocBook format, and that we wish to render this as HTML by transforming the XML to HTML through an XSLT stylesheet webapp/stylesheets/db2html.xsl. This can be done by returning the following <dispatch> element from a controller.xq:

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
  <view>
    <forward servlet="XSLTServlet">
      <set-attribute name="xslt.stylesheet" value="stylesheets/db2html.xsl"/>
    </forward>
  </view>
  <cache-control cache="no"/>
</dispatch>

In this example there are no forwarding actions except for the view, so the request will be handled by the servlet engine in the default way. The response is then passed to the XSLTServlet. A new HTTP POST request is created whose body is set to the response data of the previous step. The XSLTServlet gets the path to the stylesheet from the request attribute xslt.stylesheet and applies it to the provided data.

If any step in the pipeline generates an error or returns an HTTP status code >= 400, the pipeline processing stops and the response is send back to the client immediately. The same happens if the first step returns with an HTTP status 304 (NOT MODIFIED), which indicates that the client can use the version it has cached.

We can also pass a request through more than one view. The following document applies two stylesheets in sequence:

<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
	<!-- NOTE: query results are passed to XSLT servlet via a request attribute -->
	<set-attribute name="xquery.attribute" value="model"/>
	<view>
		<forward servlet="XSLTServlet">
			<set-attribute name="xslt.input" value="model"/>
			<set-attribute name="xslt.stylesheet" value="xquery/stylesheets/acronyms.xsl"/>
		</forward>
		<forward servlet="XSLTServlet">
			<clear-attribute name="xslt.input"/>
			<set-attribute name="xslt.stylesheet" value="stylesheets/db2html.xsl"/>
		</forward>
	</view>
</dispatch>

The example also demonstrates how information can be passed between actions. XQueryServlet (which is called implicitly because the URL ends with .xq) can save the results of the called XQuery to a request attribute instead of writing them to the HTTP output stream. It does so if it finds a request attribute named xquery.attribute, which should in turn contain the name of the request attribute that the output should be saved to.

In the example above, xquery.attribute is set to model. This causes XQueryServlet to fill the request attribute model with the results of the XQuery it executes. The query result will not be written to the HTTP response as you might expect, instead at this point the HTTP response body remains empty as the data is inside the request attribute.

Likewise, XSLTServlet can take its input from a request attribute instead of parsing the HTTP request body. The name of the request attribute should be given in attribute xslt.model. XSLTServlet discards the current request content (which is empty anyway) and uses the data in the attribute's value as input for the transformation process.

XSLTServlet will always write to the HTTP response. The second invocation of XSLTServlet therefore needs to read its input from the HTTP request body which contains the response of the first servlet. Since request attributes are preserved throughout the entire pipeline, we need to clear the xslt.input with an explicit call to clear-attribute.

The benefit of exchanging data through request attributes is that we save one serialization step: XQueryServlet directly passes the node tree of its output as a valid XQuery value, so XSLTServlet does not need to parse it again.

The advantages become more obvious if you have two or more XQueries which need to exchange information: XQuery 1 can use the XQuery extension function request:set-attribute() to save an arbitrary XQuery sequence to an attribute. XQuery 2 then subsequently calls request:get-attribute() to retrieve this value. As it can directly access the data passed in from XQuery 1, no time is lost serializing and deserializing the data.

Let's have a look at a more complex example: eXist-db's eXide web application needs to execute a user-supplied XQuery fragment. The results should be retrieved in an asynchronous way, so the user doesn't need to wait and the web interface remains usable.

Older versions of eXide's ancestor sandbox application used the util:eval() function to evaluate the query. However, this had side-effects because util:eval() executes the query within the context of the parent query. Some features like module imports could not work properly. To avoid util:eval(), the controller code below passes the user-supplied query to XQueryServlet first, then post-processes each returned result and stores it into a session for later use by the AJAX frontend:

if (starts-with($path, "/eXide/execute"))
then
  let $query := request:get-parameter("qu", ())
  let $startTime := util:system-time()
  return
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
      <!-- Query is executed by XQueryServlet -->
      <forward servlet="XQueryServlet">
        <!-- Query is passed via the attribute "xquery.source" -->
        <set-attribute name="xquery.source" value="{$query}"/>
        <!-- Results should be written into attribute "results" -->
        <set-attribute name="xquery.attribute" value="results"/>
        <!-- Errors should be passed through instead of terminating the request -->
        <set-attribute name="xquery.report-errors" value="yes"/>
      </forward>
      <view>
        <!-- Post process the result: store it into the HTTP session and return the number of hits only. -->
        <forward url="session.xq">
          <clear-attribute name="xquery.source"/>
          <clear-attribute name="xquery.attribute"/>
          <set-attribute name="elapsed" value="{string(seconds-from-duration(util:system-time() - $startTime))}"/>
        </forward>
      </view>
    </dispatch>
else
  if (starts-with($path, "/sandbox/results/"))
  then
    (: Retrieve an item from the query results stored in the HTTP session. The
      format of the URL will be /sandbox/results/X, where X is the number of the
      item in the result set :)
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
      <forward url="../session.xq">
        <add-parameter name="num" value="{$name}"/>
      </forward>
    </dispatch>

The client passes the user-supplied query string in a request parameter, so the controller has to forward this to XQueryServlet somehow. XQueryServlet has an option to read the XQuery source from a request attribute, xquery.source. The query result will be saved to the attribute results. The second XQuery, session.xq, takes the result and stores it into an HTTP session, returning only the number of hits and the elapsed time.

When called through retrieve, session.xq looks at parameter num and returns the item at the corresponding position from the query results stored in the HTTP session.

Special Attributes Accepted by eXist-db Servlets

eXist-db's XQueryServlet as well as the XSLTServlet expect a few predefined request attributes. The names of these attributes are listed below, and are reserved, that is to say that they should not be used for other purposes.

XQueryServlet

xquery.attribute

Contains the name of a request attribute. Instead of writing query results to the response output stream, XQueryServlet will store them into the named attribute. The value of the attribute will be an XQuery Sequence. If no query results were returned, the attribute will contain an empty sequence.

xquery.source

If set, the value of this attribute must contain the XQuery code to execute. Normally, XQueryServlet reads the XQuery from the file given in the request path. Use of the xquery.source attribute allows you to overwrite this behaviour, e.g. if you want to evaluate an XQuery which was generated within the controller.

xquery.module-load-path

The path which will be used for locating modules. This is only relevant in combination with xquery.source and tells the XQuery engine where to look for modules imported by the query. For example, if you stored required modules into the database collection /db/test, you can set xquery.module-load-path to xmldb:exist:///db/test. If the query contains an expression:

import module namespace test="http://exist-db.org/test" at "test.xq";

The XQuery engine will try to find the module test.xq in the filesystem by default, which may not be what you were expecting! Setting the xquery.module-load-path allows you to configure this.

xquery.report-errors

If set to yes, an error in the XQuery will not result in an HTTP error. Instead, the string message of the error is enclosed in an element <error> and then written to the response stream. The HTTP Response's Status Code will remain unchanged.

XSLTServlet

xslt.stylesheet

The path to the XSLT stylesheet. Relative paths will be resolved against the current request URL, absolute paths against the context of the web application (/exist). To reference a stylesheet which is stored in the database, use an XML:DB URL like xmldb:exist:///db/styles/myxsl.xslt.

xslt.input

Contains the name of a request attribute from which the input to the transformation process should be taken. The input has to be a valid eXist-db XQuery Sequence.

This attribute is usually combined with the xquery.attribute attribute provided by XQueryServlet and allows passing data between the two without additional serialization or deserialization overhead.

xslt.user

The name of the eXist -db user account to use when reading and executing the stylesheet.

xslt.password

The password for the user given in xslt.user

XSLTServlet will attempt to map all other request attributes starting with the prefix xslt. into stylesheet parameters. So, for example, if you set a request attribute xslt.myattr it will be available within the stylesheet as parameter $xslt.myattr. For security reasons, this is the only way to pass request parameters into the stylesheet; you can use your controller.xq to transform a HTTP Request parameter into a request attribute and pass that on to the view.