-
Notifications
You must be signed in to change notification settings - Fork 66
MVC: Roxy URL Rewriting
#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
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
.
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>;
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>;
To write your own rewriting routes, add <request> elements, using this structure:
Each request element corresponds to an individual URI pattern that you want to configure.
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.
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.
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.
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.
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.
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> ;
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.