I just went through the exercise of figuring out what’s needed to change the default page when building an application with MarkLogic’s REST API. Time to share.
Built-in Default
Suppose you have an HTTP application server configured to use the REST API (for instance, by generating a Roxy application with –app-type=rest). The URL rewriter field will be /MarkLogic/rest-api/rewriter.xqy. If you point your browser to “/”, you’ll get the REST API’s default page:
MarkLogic REST Server
- Search and retrieve XML results -Â /v1/search?format=xml
- Search and retrieve JSON results -Â /v1/search?format=json
- Search example -Â /v1/search?q=&start=10&pageLength=5
- Query Config -Â /v1/config/query
- Transform Config -Â /v1/config/transforms
- Add document (HTTP PUT)
- Add JSON document: curl -v –digest –user admin:***** -H “Content-Type: application/json” -X PUT -d ‘{“person”:{“first”:”John”, “last”:”Doe”}}’ “http://localhost:8003/v1/documents?uri=/docs/person.json”
- Add XML document: curl -v –digest –user admin:***** -H “Content-Type: application/xml” -X PUT -d ‘JohnDoe’ “http://localhost:8003/v1/documents?uri=/docs/person.xml”
- View document (HTTP GET)
- View JSON document:Â /v1/documents?uri=/docs/person.json
- View XML document:Â /v1/documents?uri=/docs/person.xml
- Remove document (HTTP DELETE)
- Remove JSON document: curl -v –digest –user admin:***** -H “Content-Type: application/json” -X DELETE “http://localhost:8003/v1/documents?uri=/docs/person.json”
- Remove XML document: curl -v –digest –user admin:***** -H “Content-Type: application/xml” -X DELETE “http://localhost:8003/v1/documents?uri=/docs/person.xml”
HTML Default
Now suppose you want a more interesting default page. You can create an HTML page called index.html. This *should* be automatically picked up as the home page, but there’s a bug that prevents that from happening (already filed with MarkLogic). The work around is to do the same thing for index.html that you would do for foo.html.
The REST API’s rewriter looks for a set of rewrite rules at /application/custom/rewrite.xml. The specific path is a reflection of the structure used by Application Builder, but it will work for any REST-based application. Here’s what to put inside:
<?xml version="1.0" encoding="UTF-8"?> <rules xmlns="http://marklogic.com/appservices/app"> <content> <uri-step>default</uri-step> <content-source>/foo.html</content-source> </content> </rules>
The content source can be any HTML file you like, with any path in your modules database (including /index.html). (Once the bug gets fixed, you can use /index.html and skip this step all together.)
XQuery Default
For now, I’ll leave aside the question of when you would want to directly hit XQuery endpoints while also working with the REST API; I’ll just show you how to do it.
Working with the REST API’s Rewriter
In this case, we want some XQuery main module, say, main.xqy, to be the default page for our REST API-based application. Let’s create a simple main.xqy:
xquery version "1.0-ml";
<html> <body> <h2>This is XQuery</h2> </body> </html>
and we’ll update rewrite.xml to point to it:
<?xml version="1.0" encoding="UTF-8"?> <rules xmlns="http://marklogic.com/appservices/app"> <content> <uri-step>default</uri-step> <content-source>/main.xqy</content-source> </content> </rules>
When we run this, the XQuery module gets dumped to the browser as text. Really not what we’re looking for. The reason this happens is because the REST API’s rewriter assumes that you’re either returning a document that the browser knows how to handle (HTML), or that the document will be passed through an XSLT transformation, resulting in something the browser can handle (XML -> HTML). Neither of these cases handles simply eval’ing an XQuery module. But suppose you want to anyway. There’s a trick we can apply to make this work. We’re going to make a change to rewrite.xml:
<?xml version="1.0" encoding="UTF-8"?> <rules xmlns="http://marklogic.com/appservices/app"> Â <master-xsl>/application/app.xsl</master-xsl> <content> <uri-step>default</uri-step> <content-source>/main.xqy</content-source> </content> </rules>
and then add the app.xsl file:
<xsl:stylesheet version="2.0" exclude-result-prefixes="xdmp" extension-element-prefixes="xdmp" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xdmp="http://marklogic.com/xdmp">
<xsl:template match="@*|*|processing-instruction()|comment()"> <xsl:copy> <xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()"/> </xsl:copy> </xsl:template>
<xsl:template match="/"> <xsl:apply-templates select="xdmp:eval(.)"/> </xsl:template> </xsl:stylesheet>
This is the simplest version of the XSL that I could get to work. (I couldn’t tell you why the first xsl:template match is necessary, but this doesn’t work without it.) The key is the line with the xdmp:eval(.) in it. That runs the XQuery main module, resulting in the expected output.
Using Your Own Rewriter
Above I referred to a trick to get the REST API’s rewriter to work with XQuery modules. The approach goes against the grain of what that rewriter is intended to do. On the other hand, it’s a little tricky to blend XQuery endpoints and REST API endpoints in a single rewriter. Roxy Hybrid applications pull this off using Roxy’s custom rewriter, which attempts to rewrite using a set of rules specified in the application. If none match, Roxy hands off to the REST API rewriter looking for a match. REST API requests are handled using this deferral. This rewriter generally assumes that incoming requests will map to MVC requests, but you can add a rule to src/app/config/config.xqy to allow direct access to arbitrary XQuery modules:
<request uri="^/(.*\.xqy)" endpoint="/$1"/>
If this sounds like what you want to do, you can get started with a Roxy hybrid app like this:
$ ml new my-app --app-type=hybrid --server-version=7
Replace the application name (“my-app”) and the server version with appropriate values, and you’re off and running.
Tags: marklogic, rest api, roxy
September 4th, 2013 at 12:21 pm
Just wanted to add some comments to help provide context for some readers since I would guess a lot of users of the MarkLogic REST API are not using the MarkLogic HTTP Application Server to host their front-end application assets (e.g. HTML, JavaScript, CSS). This post is only applicable for people who want to use the MarkLogic HTTP Application Server to serve their front-end assets.
Why would someone want to serve HTML/JS/CSS out of a MarkLogic instance? It may seem strange for some to think of MarkLogic as anything more than a NoSQL datasource but it also provides a fully-featured HTTP application server. It simplifies the architecture by reducing the number of moving pieces. The MarkLogic HTTP application server may not have some features that a standalone HTTP web server such as Apache, IIS, or NGINX may have.