Skip to content
This repository has been archived by the owner on Nov 9, 2022. It is now read-only.

MVC: Roxy URL Rewriting

Geert edited this page Jun 8, 2017 · 1 revision

#Roxy URL Conventions For MVC and Hybrid applications, Roxy uses the following convention out of the box for URL rewriting.

http://server:port/controller/function.format

controller - the name of a controller to execute $roxy/app/controller/controller.xqy
function - the name of a function within controller to execute
format - the format in which to render results (html, json, txt)

Roxy will then call the specified function within the specified controller.

Example:

http://server:port/search/find.html
    Will call the find() function of the search controller.
    The output of find() is passed to the html view: /src/app/views/search/find.html.xqy

If you don't provide a function the default function is assumed. This is typically main(), but can be overridden in src/app/config/config.xqy.

Example:

http://server:port/search.html
    Will call the main() function of the search controller.
    The output of main() is passed to the html view: /src/app/views/search/main.html.xqy

If you don't provide a format, HTML is assumed. This default can be changed in /src/app/config/config.xqy

Example:

http://server:port/search
    Will call the main() function of the search controller.
    The output of main() is passed to the html view: /src/app/views/search/main.html.xqy

Verbs

Roxy uses HTTP verbs to determine whether to run your query in update or query mode. See the MarkLogic docs for more information on query vs update mode.

Roxy uses the following conventions for HTTP verbs:

VERB QUERY MODE
GET query
HEAD query
POST update
PUT update
DELETE update

#OVERRIDING ROXY URL ROUTES It is possible to extend or replace the default routing built into Roxy by editing src/app/config/config.xqy.

Extending the Routes for URL rewriting

The built-in rules provide mappings for the controller/function routing and for the css, images, and js directories under public. If you want to supplement that, you can add your own rules before the built-in ones are included. For example, suppose you want to put the MarkLogic Visualization Widgets into your public directory in a directory called mlviz and that you want a public/html directory with simple html pages in it.

declare variable $ROXY-ROUTES :=
  <routes xmlns="http://marklogic.com/appservices/rest">
    <request uri="^/mlviz/(.*)" endpoint="/public/mlviz/$1"/>
    <request uri="^/html/(.*)" endpoint="/public/html/$1"/>
    {
      $def:ROXY-ROUTES/rest:request
    }
  </routes>;

Replacing the Routes for URL rewriting with your own

In other cases, you might want to replace the built-in routes with your own set. In that case, you would delete the reference to $def:ROXY-ROUTES/rest:request and just add your own routes. You might do this if you've decided to use a different way of mapping from URIs to XQuery functions.

Here's the mlviz example again, but this time with the default routes removed. As written, the Visualization Widgets are the only URLs this will handle.

declare variable $ROXY-ROUTES :=
  <routes xmlns="http://marklogic.com/appservices/rest">
    <request uri="^/mlviz/(.*)" endpoint="/public/mlviz/$1"/>
  </routes>;

Route Structure

To write your own rewriting routes, add <request> elements, using this structure:

Request elements

Each request element corresponds to an individual URI pattern that you want to configure.

request/@uri attribute

The uri attribute on the request element is a regular expression pattern that incoming requests will be matched against. The fn:matches() function will be used to compare the pattern to incoming URIs. The pattern may use parentheses.

request/@endpoint

The endpoint attribute specifies the rewritten URL. If patterns were used in the uri attribute pattern, the values captured in them can be use in the new URL. Example:

<request uri="^/(css|js|images)/(.*)" endpoint="/public/$1/$2"/>

If the endpoint attribute is not present, the rewriter will not alter the URL. This is useful for specifying URLs that should be excluded from rewriting.

request/@redirect

The redirect attribute tells the browser to redirect its request to a different URL. Note that the revised URL will be sent to the client, and the browser will display the new URL.

request/uri-param

To convert parts of the URI into URI parameters for the module that will handle the request, add uri-param child elements under the request. Example:

<request uri="^/book/view(\.?\w*)/(\d+)" endpoint="/book/view$1">
  <uri-param name="id">$2</uri-param>
<request>

The uri-param element may optionally have a default attribute, providing a default value for the URI parameter.

request/http/@method

The http child element's method attribute makes the request only match HTTP requests made with that verb. This allows the same URL to direct requests differently for GET and POST requests. All HTTP verbs are supported.

Example

Here's an example showing all of the above:

declare variable $c:ROXY-ROUTES := 
  <routes xmlns="http://marklogic.com/appservices/rest">
    <request uri="^/book/(\d+)" redirect="/book/view">
      <uri-param name="id">$1</uri-param>
      <http method="GET"/>
      <http method="HEAD"/>
    </request>
    <request uri="^/book/(\d+)" endpoint="/roxy/update-router.xqy">
      <uri-param name="controller">book</uri-param>
      <uri-param name="func">update</uri-param>
      <uri-param name="id">$1</uri-param>
      <http method="PUT"/>
    </request>
    <request uri="^/book/(\d+)" redirect="/book/delete">
      <uri-param name="id">$1</uri-param>
      <http method="DELETE"/>
    </request>
    { $def:ROXY-ROUTES/rest:request }
  </routes>
;

Ruby on Rails style Resources

We implemented resource routing to mimic what Ruby on Rails does. See Here for the Rails documentation

This table describes the 7 different routes that automatically map to a resource. This example is for a photos controller.xqy

HTTP Verb Path action used for
GET /photos index display a list of all photos
GET /photos/new new return an HTML form for creating a new photo
POST /photos create create a new photo
GET /photos/:id show display a specific photo
GET /photos/:id/edit edit return an HTML form for editing a photo
PUT /photos/:id update update a specific photo
DELETE /photos/:id destroy delete a specific photo

To create a resource for photos in Roxy simple edit src/app/config/config.xqy like so

(: this goes in src/app/config/config.xqy :)

import module namespace def = "http://marklogic.com/roxy/defaults" at "/roxy/config/defaults.xqy";

declare namespace rest = "http://marklogic.com/appservices/rest";

...

declare variable $c:ROXY-ROUTES := 
  <routes xmlns="http://marklogic.com/appservices/rest">
    <request resource="photos" />
    { $def:ROXY-ROUTES/rest:request }
  </routes>
;

Now you need to edit your src/app/controllers/photos.xqy controller to support the following methods:

(: this goes in src/app/controllers/photos.xqy :)

xquery version "1.0-ml";

module namespace c = "http://marklogic.com/roxy/controller/photos";

declare function c:index()
{
  (: list all of your photos :)
};

declare function c:new()
{
  (: return a form to create a new photo :)
};

declare function c:create()
{
  (: create a new photo from the given HTTP request parameters :)
};

declare function c:show()
{
  (: return the photo with the given id :)
  let $id := req:get("id")
  ...
};

declare function c:edit()
{
  (: return a form to edit the photo with the given id :)
  let $id := req:get("id")
  ...
};

declare function c:update()
{
  (: update the photo with the given id :)
  let $id := req:get("id")
  let $body := xdmp:get-request-body()
  ...
};

declare function c:destroy()
{
  (: delete the photo with the given id :)
  let $id := req:get("id")
  ...
};

#Using Your Own Rewriter If the provided rewriter doesn't do what you want, you have the option to by-pass it. You can write your own rewriter module, then in deploy/build.properties set the url-rewriter property to point to it. Put your module outside the src/roxy directory to upgrades easy.

# in deploy/build.properties
url-rewriter=/app/rewriter.xqy

The next time you bootstrap, your app server will start using your new rewriter.