The REST Library enables you to create RESTful functions that are independent of the language used in applications.
The procedures in this chapter assume you performed the steps described in Preparing to Run the Examples.
The topics in this section are:
The REST Library consists of a set of XQuery functions that support URL rewriting and endpoint validation and a MarkLogic REST vocabulary that simplifies the task of describing web service endpoints. The REST vocabulary is used to write declarative descriptions of the endpoints. These descriptions include the mapping of URL parts to parameters and conditions that must be met in order for the incoming request to be mapped to an endpoint.
The REST Library contains functions that simplify:
The REST vocabulary allows you to use same description for both the rewriter and the endpoint.
When you have enabled RESTful access to MarkLogic Server resources, applications access these resources by means of a URL that invokes an endpoint module on the target MarkLogic Server host.
The REST library does the following:
If the request is valid, the endpoint module executes the requested operation and returns any data to the application. Otherwise, the endpoint module returns an error message.
The API signatures for the REST Library are documented in the MarkLogic XQuery and XSLT Function Reference. For additional information on URL rewriting, see Setting Up URL Rewriting for an HTTP App Server.
This section describes a simple rewriter script that calls a single endpoint.
Navigate to the /<
MarkLogic_Root>/bill
directory and create the following files with the described content.
Create a module, named requests.xqy
, with the following content:
xquery version "1.0-ml"; module namespace requests="http://marklogic.com/appservices/requests"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; declare variable $requests:options as element(rest:options) := <options xmlns="http://marklogic.com/appservices/rest"> <request uri="^/(.+)$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> </request> </options>;
Create a module, named url_rewriter.xqy
, with the following content:
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; rest:rewrite($requests:options)
Create a module, named endpoint.xqy
, with the following content:
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; let $request := $requests:options/rest:request [@endpoint = "/endpoint.xqy"][1] let $map := rest:process-request($request) let $play := map:get($map, "play") return fn:doc($play)
Enter the following URL, which uses the bill
App Server created in Preparing to Run the Examples:
http://localhost:8060/macbeth
The rest:rewrite function in the rewriter uses an options
node to map the incoming request to an endpoint. The options
node includes a request
element with a uri
attribute that specifies a regular expression and an endpoint
attribute that specifies the endpoint module to invoke in the event the URL of an incoming request matches the regular expression. In the event of a match, the portion of the URL that matches (.+)
is bound to the $1
variable. The uri-param
element in the request
element assigns the value of the $1
variable, along with the .xml
extension, to the play
parameter.
<rest:options xmlns="http://marklogic.com/appservices/rest"> <rest:request uri="^/(.+)" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> </rest:request> </rest:options>
In the example rewriter module above, this options
node is passed to the rest:rewrite function, which outputs a URL that calls the endpoint module with the parameter play=macbeth.xml
:
/endpoint.xqy?play=macbeth.xml
The rest:process-request function in the endpoint locates the first request
element associated with the endpoint.xqy
module and uses it to identify the parameters defined by the rewriter. In this example, there is only a single parameter, play
, but for reasons described in Extracting Multiple Components from a URL, when there are multiple request
elements for the same endpoint, then the request
element that extracts the greatest number of parameters from a URL should be listed in the options
node ahead of those that extract fewer parameters.
let $request := $requests:options/rest:request [@endpoint = "/endpoint.xqy"][1]
The rest:process-request function in the endpoint uses the request
element to parse the incoming request and return a map that contains all of the parameters as typed values. The map:get function extracts each parameter from the map, which is only one in this example.
let $map := rest:process-request($request) let $play := map:get($map, "play")
The default behavior for the rewriter is to match the request against all of the criteria: URI, accept headers, content-type, conditions, method, and parameters. This assures that no endpoint will ever be called except in circumstances that perfectly match what is expected. It can sometimes, however, lead to somewhat confusing results. Consider the following request node:
<request uri="/path/to/resource" endpoint="/endpoint.xqy"> <param name="limit" as="decimal"/> </request>
An incoming request of the form:
/path/to/resource?limit=test
does not match that request node (because the limit is not a decimal). If there are no other request nodes which match, then the request will return 404 (not found).
That may be surprising. Using additional request nodes to match more liberally is one way to address this problem. However, as the number and complexity of the requests grows, it may become less attractive. Instead, the rewriter can be instructed to match only on specific parts of the request. In this way, error handling can be addressed by the called module.
The match criteria are specified in the call to rest:rewrite. For example:
rest:rewrite($options, ("uri", "method"))
In this case, only the URI and HTTP method will be used for the purpose of matching.
uri
= match on the URIaccept
= match on the accept headerscontent-type
= match on the content typeconditions
= match on the conditionsmethod
= match on the HTTP methodparams
= match on the paramsThe request must match all of the criteria specified in order to be considered a match.
The REST Library uses an options
node to map incoming requests to endpoints. The options
node must be declared as type element(rest:options)
and must be in the http://marklogic.com/appservices/rest
namespace.
Below is a declaration of a very simple options node:
declare variable $options as element(rest:options) := <rest:options xmlns="http://marklogic.com/appservices/rest"> <rest:request uri="^/(.+)" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> </rest:request> </rest:options>;
The table below summarizes all of the possible elements and attributes in an options
node. On the left are the elements that have attributes and/or child elements. Attributes for an element are listed in the Attributes column. Attributes are optional, unless designated as '(required)'. Any first-level elements of the element listed on the left are listed in the Child Elements column. The difference between the user-params="allow"
and user-params="allow-dups"
attribute values is that allow
permits a single parameter for a given name, and allow-dups
permits multiple parameters for a given name.
Element | Attributes | Child Elements | Number of Children | For More Information |
---|---|---|---|---|
options |
user-params = ignore | allow | allow-dups | forbid |
request |
0..n |
A Simple XQuery Rewriter and Endpoint |
request |
uri=string endpoint=string user-params = ignore | allow | allow-dups | forbid |
uri-param param http auth function accept user-agent and or |
0..n 0..n 0..n 0..n 0..n 0..n 0..n 0..n 0..n |
A Simple XQuery Rewriter and Endpoint |
uri-param |
name=string (required) as=string |
|||
param |
name=string (required) as=string values=string match=string default=string required = true | false repeatable = true | false pattern = <regex> |
Supporting Parameters Specified in a URL. Matching Regular Expressions in Parameters with the match and pattern Attributes |
||
http |
method = string (required) user-params = ignore | allow | allow-dups | forbid |
param auth function accept user-agent and or |
0..n 0..n 0..n 0..n 0..n 0..n 0..n |
|
auth |
privilege kind |
0..n 0..n |
Authentication Condition | |
function |
ns=string (required) apply=string (required) at=string (required) |
Function Condition | ||
You can use the rest:check-options function to validate an options
node against the REST schema. For example, to validate the options
node defined in the requests.xqy
module described in A Simple XQuery Rewriter and Endpoint, you would do the following:
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; rest:check-options($requests:options)
An empty sequence is returned if the options
node is valid. Otherwise an error is returned.
You can also use the rest:check-request function to validate request
elements in an options
node. For example, to validate all of the request
elements in the options
node defined in the requests.xqy
module described in A Simple XQuery Rewriter and Endpoint, you would do the following:
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; declare option xdmp:mapping "false"; rest:check-request($requests:options/rest:request)
An empty sequence is returned if the request
elements are valid. Otherwise an error is returned.
Before calling the rest:check-request function, you must set xdmp:mapping
to false
to disable function mapping.
An options
node may include one or more request
elements, each of which may contain one or more uri-param
elements that assign parameters to parts of the request URL. The purpose of each request
element is to detect a particular URL pattern and then call an endpoint with one or more parameters. Extracting multiple components from a URL is simply a matter of defining a request
element with a regular expression that recognizes particular URL pattern and then binding the URL parts of interest to variables.
For example, you want expand the capability of the rewriter described in A Simple XQuery Rewriter and Endpoint and add the ability to use a URL like the one below to display an individual act in a Shakespeare play:
http://localhost:8060/macbeth/act3
The options
node in requests.xqy
might look like the one below, which contains two request
elements. The rewriter employs a first-match rule, which means that it tries to match the incoming URL to the request
elements in the order they are listed and selects the first one containing a regular expression that matches the URL. In the example below, if an act is specified in the URL, the rewriter uses the first request
element. If only a play is specified in the URL, there is no match in the first request
element, but there is in the second request
element.
The default parameter type is string. Non-string parameters must be explicitly typed, as shown for the act
parameter below. For more information on typing parameters, see Parameter Types.
<options> <request uri="^/(.+)/act(\d+)$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> <uri-param name="act" as="integer">$2</uri-param> </request> <request uri="^/(.+)/?$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> </request> </options>
When an act is specified in the incoming URL, the first request
element binds macbeth
and 3
to the variables $1
and $2
, respectively, and then assigns them to the parameters named, play
and act
. The URL rewritten by the rest:rewrite function looks like:
/endpoint.xqy?play=macbeth.xml&act=3
The following is an example endpoint module that can be invoked by a rewriter that uses the options
node shown above. As described in A Simple XQuery Rewriter and Endpoint, the rest:process-request function in the endpoint uses the request
element to parse the incoming request and return a map that contains all of the parameters as typed values. Each parameter is then extracted from the map by means of a map:get function. If the URL that invokes this endpoint does not include the act
parameter, the value of the $num
variable will be an empty sequence.
The first request
element that calls the endpoint.xqy
module is used in this example because, based on the first-match rule, this element is the one that supports both the play
and act
parameters.
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; let $request := $requests:options/rest:request [@endpoint = "/endpoint.xqy"][1] let $map := rest:process-request($request) let $play := map:get($map, "play") let $num := map:get($map, "act") return if (empty($num)) then fn:doc($play) else fn:doc($play)/PLAY/ACT[$num]
The REST endpoint library includes a rest:report-error function that performs a simple translation of MarkLogic Server error markup to HTML. You can invoke it in your modules to report errors:
try { let $params := rest:process-request($request) return ...the non-error case... } catch ($e) { rest:report-error($e) }
If the user agent making the request accepts text/html, a simple HTML-formatted response is returned. Otherwise, it returns the raw error XML.
You can also use this function in an error handler to process all of the errors for a particular application.
As shown in the previous sections of this chapter, the URL rewriter translates the requested URL into a new URL for dispatching within the server. The user agent making the request is totally unaware of this translation. As REST Librarys mature and expand, it is sometimes useful to use redirection to respond to a request by telling the user agent to reissue the request at a new URL.
For example, previous users accessed the macbeth play using the following URL pattern:
http://localhost:8060/Shakespeare/macbeth
You want to redirect the URL to:
http://localhost:8060/macbeth
The user can tell that this redirection happened because the URL in the browser address bar changes from the old URL to the new URL, which can then be bookmarked by the user.
You can support such redirects by adding a redirect.xqy
module like this one to your application:
xquery version "1.0-ml"; import module namespace rest="http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; (: Process requests to be handled by this endpoint module. :) let $request := $requests:options/rest:request [@endpoint = "/redirect.xqy"][1] let $params := rest:process-request($request) (: Get parameter/value map from request. :) let $query := fn:string-join( for $param in map:keys($params) where $param != "__ml_redirect__" return for $value in map:get($params, $param) return fn:concat($param, "=", fn:string($value)), "&") (: Return the name of the play along with any parameters. :) let $ruri := fn:concat(map:get($params, "__ml_redirect__"), if ($query = "") then "" else fn:concat("?", $query)) (: Set response code and redirect to new URL. :) return (xdmp:set-response-code(301, "Moved permanently"), xdmp:redirect-response($ruri))
In the options
node in requests.xqy
, add the following request elements to perform the redirect:
<request uri="^/shakespeare/(.+)/(.+)" endpoint="/redirect.xqy"> <uri-param name="__ml_redirect__">/$1/$2</uri-param> </request> <request uri="^/shakespeare/(.+)" endpoint="/redirect.xqy"> <uri-param name="__ml_redirect__">/$1</uri-param> </request>
Your options
node should now look like the one shown below. Note that the request
elements for the redirect.xqy
module are listed before those for the endpoint.xqy
module. This is because of the first-match rule described in Extracting Multiple Components from a URL.
<options xmlns="http://marklogic.com/appservices/rest"> <request uri="^/shakespeare/(.+)/(.+)" endpoint="/redirect.xqy"> <uri-param name="__ml_redirect__">/$1/$2</uri-param> </request> <request uri="^/shakespeare/(.+)" endpoint="/redirect.xqy"> <uri-param name="__ml_redirect__">/$1</uri-param> </request> <request uri="^/(.+)/act(\d+)" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> <uri-param name="act" as="integer">$2</uri-param> </request> <request uri="^/(.+)$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> </request> </options>
You can employ as many redirects as you want through the same redirect.xqy
module by changing the value of the __ml_redirect__
parameter.
A request that doesn't specify any verbs only matches HTTP GET requests. If you want to match other verbs, simply list them by using the http
element with a method
attribute in the request
element:
<request uri="^/(.+?)/?$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> <http method="GET"/> <http method="POST"/> </request>
This request will match (and validate) if the request method is either an HTTP GET or an HTTP POST.
The following topics describe use cases for mapping requests with verbs and simple endpoints that service those requests:
You may find it useful to have a mechanism that returns the options
node or a specific request
element in the options
node. For example, you could automate some aspects of unit testing based on the ability to find the request
element that matches a given URL. You can implement this type of capability by supporting the OPTIONS
method.
Below is a simple options.xqy
module that handles requests that specify an OPTIONS
method. If the request URL is /, the options.xqy
module returns the entire options
element, exposing the complete set of endpoints. When the URL is not /, the module returns the request
element that matches the URL.
xquery version "1.0-ml"; import module namespace rest="http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; (: Process requests to be handled by this endpoint module. :) let $request := $requests:options/rest:request [@endpoint = "/options.xqy"][1] (: Get parameter/value map from request. :) let $params := rest:process-request($request) let $uri := map:get($params, "__ml_options__") let $accept := xdmp:get-request-header("Accept") let $params := map:map() (: Get request element that matches the specified URL. :) let $request := rest:matching-request($requests:options, $uri, "GET", $accept, $params) (: If URL is '/', return options node. Otherwise, return request element that matches the specified URL. :) return if ($uri = "/") then $requests:options else $request
Add the following request
element to requests.xqy
to match any HTTP request that includes an OPTIONS
method.
<request uri="^(.+)$" endpoint="/options.xqy" user-params="allow"> <uri-param name="__ml_options__">$1</uri-param> <http method="OPTIONS"/> </request>
Open Query Console and enter the following query, replacing name and password with your login credentials:
xdmp:http-options("http://localhost:8011/", <options xmlns="xdmp:http"> <authentication method="digest"> <username>name</username> <password>password</password> </authentication> </options>)
Because the request URL is /, the entire options
node should be returned. To see the results when another URL is used, enter the following query in Query Console:
xdmp:http-options("http://localhost:8011/shakespeare/macbeth", <options xmlns="xdmp:http"> <authentication method="digest"> <username>name</username> <password>password</password> </authentication> </options>)
Rather than the entire options
node, the request
element that matches the given URL is returned:
<request uri="^/shakespeare/(.+)" endpoint="/redirect.xqy" xmlns="http://marklogic.com/appservices/rest"> <uri-param name="__ml_redirect__">/$1</uri-param> </request>
You can use it by adding the following request to the end of your options:
<request uri="^(.+)$" endpoint="/options.xqy" user-params="allow"> <uri-param name="__ml_options__">$1</uri-param> <http method="OPTIONS"/> </request>
If some earlier request directly supports OPTIONS
then it will have priority for that resource.
You may want the ability to support the POST
method to implement RESTful content management features, such as loading content into a database.
Below is a simple post.xqy
module that accepts requests that include the POST
method and inserts the body of the request into the database at the URL specified by the request.
xquery version "1.0-ml"; import module namespace rest="http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; (: Process requests to be handled by this endpoint module. :) let $request := $requests:options/rest:request [@endpoint = "/post.xqy"][1] (: Get parameter/value map from request. :) let $params := rest:process-request($request) let $posturi := map:get($params, "_ml_post_") let $type := xdmp:get-request-header('Content-Type') (: Obtain the format of the content. :) let $format := if ($type = 'application/xml' or ends-with($type, '+xml')) then "xml" else if (contains($type, "text/")) then "text" else "binary" (: Insert the content of the request body into the database. :) let $body := xdmp:get-request-body($format) return (xdmp:document-insert($posturi, $body), concat("Successfully uploaded: ", $posturi, " "))
Add the following request element to requests.xqy
. If the request URL is /post/
filename, the rewriter will issue an HTTP request to the post.xqy
module that includes the POST
method.
<request uri="^/post/(.+)$" endpoint="/post.xqy"> <uri-param name="_ml_post_">$1</uri-param> <http method="POST"/> </request>
To test the post.xqy
endpoint, open Query Console and enter the following query, replacing 'name' and 'password' with your MarkLogic Server login credentials:
let $document:= xdmp:quote( <html> <title>My Document</title> <body> This is my document. </body> </html>) return xdmp:http-post("http://localhost:8011/post/mydoc.xml", <options xmlns="xdmp:http"> <authentication method="digest"> <username>name</username> <password>password</password> </authentication> <data>{$document}</data> <headers> <content-type>text/xml</content-type> </headers> </options>)
Click the Query Console Explore button and locate the /mydoc.xml
document in the Documents
database.
This section details the uri-param
and param
elements in a request
element. The topics in this section are:
By default, a parameter is typed as a string. Other types of parameters, such as integers or booleans, must be explicitly typed in the request element. Using the example request
element from Extracting Multiple Components from a URL, the act
parameter must be explicitly defined as an integer.
<request uri="^/(.+)/act(\d+)$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> <uri-param name="act" as="integer">$2</uri-param> </request>
You can define a parameter type using any of the types supported by XQuery, as described in the specification, XML Schema Part 2: Datatypes Second Edition:
The REST Library supports parameters entered after the URL path with the following format:
http://host:port/url-path?param=value
For example, you want the endpoint.xqy
module to support a "scene" parameter, so you can enter the following URL to return Macbeth, Act 4, Scene 2:
http://localhost:8011/macbeth/act4?scene=2
To support the scene
parameter, modify the first request
element for the endpoint module as shown below. The match
attribute in the param
element defines a subexpression, so the parameter value is assigned to the $1
variable, which is separate from the $1
variable used by the uri_param
element.
<request uri="^/(.+)/act(\d+)$" endpoint="/endpoint.xqy"> <uri-param name="play">$1.xml</uri-param> <uri-param name="act" as="integer">$2</uri-param> <param name="scene" as="integer" match="(.+)">$1</param> </request>
Rewrite the endpoint.xqy
module as follows to add support for the scene
parameter:
xquery version "1.0-ml"; import module namespace rest = "http://marklogic.com/appservices/rest" at "/MarkLogic/appservices/utils/rest.xqy"; import module namespace requests = "http://marklogic.com/appservices/requests" at "requests.xqy"; let $request := $requests:options/rest:request [@endpoint = "/requests.xqy"][1] let $map := rest:process-request($request) let $play := map:get($map, "play") let $num := map:get($map, "act") let $scene := map:get($map, "scene") return if (empty($num)) then fn:doc($play) else if (empty($scene)) then fn:doc($play)/PLAY/ACT[$num] else fn:doc($play)/PLAY/ACT[$num]/SCENE[$scene]
Now the rewriter and the endpoint will both recognize a scene
parameter. You can define any number of parameters in a request
element.
By default parameters defined by the param
element are optional. You can use the required
attribute to make a parameter required. For example, you can use the required
attribute as shown below to make the scene
parameter required so that a request URL that doesn't have a scene
will not match and an attempt to call the endpoint without a scene
raises an error.
<param name="scene" as="integer" match="(.+)" required="true"> $1 </param>
You can provide a default value for a parameter. In the example below, a request for an act without a scene parameter will return scene 1 of that act:
<param name="scene" as="integer" match="(.+)" default="1"> $1 </param>
For parameters like scene
, you may want to specify a delimited list of values. For example, to support only requests for scenes 1, 2, and 3, you would do the following:
<param name="scene" as="integer" values="1|2|3" default="1"/>
You can mark a parameter as repeatable. For example, you want to allow a css parameter to specify additional stylesheets for a particular play. You might want to allow more than one, so you could add a css parameter like this:
<param name="css" repeatable="true"/>
In the rewriter, this would allow any number of css parameters. In the endpoint, there would be a single css key in the parameters map but its value would be a list.
There may be circumstances in which you want to interpret different key values in the incoming URL as a single key value.
For example, jQuery changes the key names if the value of a key is an array. So, if you ask JQuery to invoke a call with { "a": "b", "c": [ "d", "e" ] }, you get the following URL:
http://whatever/endpoint?a=b&c[]=d&c[]=e
You can use the alias
attribute as shown below so that the map you get back from the rest:process-request function will have a key value of "c" regardless of whether the incoming URL uses c=
or c[]=
in the parameters:
As shown in Supporting Parameters Specified in a URL, you can use the match
attribute to perform the sort of match and replace operations on parameter values that you can perform on parts of the URL using the uri
attribute in the request
element. You can use the pattern
attribute to test the name of the parameter. This section goes into more detail on the use of the match
attribute and the pattern
attribute. This section has the following parts:
The match
attribute in the param
element defines a subexpression with which to test the value of the parameter, so the captured group in the regular expression is assigned to the $1
variable.
You can use the match
attribute to translate parameters. For example, you want to translate a parameter that contains an internet media type and you want to extract part of that value using the match
attribute. The following will translate format=application/xslt+xml
to format=xslt
.
<param name="format" match="^application/(.*?)(\+xml)?$"> $1 </param>
If you combine matching in parameters with validation, make sure that you validate against the transformed value. For example, this parameter will never match:
<param name="test" values="foo|bar" match="^(.+)$"> baz-$1 </param>
<param name="test" values="baz-foo|baz-bar" match="^(.+)$"> baz-$1 </param>
In other words, the value that is validated is the transformed value.
The param
element supports a pattern
attribute, which uses the specified regular expression to match the name of the parameter. This allows you to specify a regular expression for matching parameter names, for example:
pattern='xmlns:.+' pattern='val[0-9]+'
Exactly one of name
or pattern
must be specified. It is an error if the name of a parameter passed to the endpoint matches more than one pattern.
You can add conditions, either in the body of the request, in which case they apply to all verbs, or within a particular verb. For example, the request
element below contains an auth
condition for the POST
verb and a user-agent
condition for both GET
and POST
verbs.
<request uri="^/slides/(.+?)/?$" endpoint="/slides.xqy"> <uri-param name="play">$1.xml</uri-param> <http method="GET"/> <http method="POST"> <auth> <privilege>http://example.com/privs/editor</privilege> <kind>execute</kind> </auth> </http> <user-agent>ELinks</user-agent> </request>
With this request, only users with the specified execute privilege can POST
to that URL. If a user without that privilege attempts to post, this request won't match and control will fall through to the next request. In this way, you can provide fallbacks if you wish.
In a rewriter, failing to match a condition causes the request not to match. In an endpoint, failing to match a condition raises an error.
The topics in this section are:
You can add an auth
condition that checks for specific privileges using the following format:
<auth> <privilege>privilege-uri</privilege> <kind>kind</kind> </auth>
For example, the request
element described for POST
requests in Handling POST Requests allows any user to load documents into the database. To restrict this POST
capability to users with infostudio execute privilege, you can add the following auth
condition to the request element:
<request uri="^/post/(.+)$" endpoint="/post.xqy"> <uri-param name="_ml_post_">$1</uri-param> http method="POST"> <auth> <privilege> http://marklogic.com/xdmp/privileges/infostudio </privilege> <kind>execute</kind> </auth> </http> </request>
The privilege can be any specified execute or URL privilege. If unspecified, kind
defaults to execute
.
When a user agent requests a URL, it can also specify the kinds of responses that it is able to accept. These are specified in terms of media types. You can specify the media type(s) that are acceptable with the accept header.
For example, to match only user agent requests that can accept JSON responses, specify the following accept condition in the request:
<accept>application/json</accept>
You can also match on the user agent string. A request that specifies the user-agent
shown below will only match user agents that identify as the ELinks browser.
<user-agent>ELinks</user-agent>
The function
condition gives you the ability to test for arbitrary conditions. By specifying the namespace, local name, and module of a function, you can execute arbitrary code:
<function ns="http://example.com/module" apply="my-function" at="utils.xqy"/>
A request that specifies the function shown above will only match requests for which the specified function returns true. The function will be passed the URL string and the function condition element.
An and
condition must contain only conditions. It returns true if and only if all of its child conditions return true.
<and> ...conditions... </and>
If more than one condition is present at the top level in a request, they are treated as they occurred in an and
.
For example, the following condition matches only user agent requests that can accept responses in HTML from an ELinks browser:
<and> <accept>text/html</accept> <user-agent>ELinks</user-agent> </and>
There is no guarantee that conditions will be evaluated in any particular order or that all conditions will be evaluated.
An or
condition must contain only conditions. It returns true if and only if at least one of its child conditions return true.
<or> ...conditions... </or>
For example, the following condition matches only user agent requests that can accept responses in HTML or plain text:
<or> <accept>text/html</accept> <accept>text/plain</accept> </or>
There is no guarantee that conditions will be evaluated in any particular order or that all conditions will be evaluated.
A content-type
condition is a condition that returns true if the request has a matching content type. The content-type
condition is allowed everywhere that conditions are allowed.
Before you can run the examples in this chapter, you must perform the following steps:
The examples in this chapter assume you have the Shakespeare plays in the form of XML files loaded into a database. The easiest way to load the XML content into the Documents
database is to do the following:
Documents
.Run
to run the query.The following query loads the current database with the XML files in a zip file containing the plays of Shakespeare:
xquery version "1.0-ml"; import module namespace ooxml= "http://marklogic.com/openxml" at "/MarkLogic/openxml/package.xqy"; xdmp:set-response-content-type("text/plain"), let $zip-file := xdmp:document-get("http://www.ibiblio.org/bosak/xml/eg/shaks200.zip") return for $play in ooxml:package-uris($zip-file) where fn:contains($play , ".xml") return (let $node := xdmp:zip-get ($zip-file, $play) return xdmp:document-insert($play, $node) )
The XML source for the Shakespeare plays is subject to the copyright stated in the shaksper.htm file contained in the zip file.
Follow this procedure to create an HTTP App Server with which to exercise the examples in this chapter.
bill
, assign it port 8060
, specify bill
as the root directory, and Documents
as the database.bill
to hold the modules you will create as part of the examples in this chapter.