//////////////////////////////////////////////////////////////////////////////// /// @brief user guide guide /// /// @file /// /// DISCLAIMER /// /// Copyright 2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- USER MANUAL // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @page UserManual ArangoDB's User Manual (@VERSION) /// /// @NAVIGATE{Upgrading,Home,DbaManual} /// /// @if LATEX /// /// @else /// @copydetails FirstStepsArangoDBTOC /// @copydetails UserManualArangoshTOC /// @copydetails ShellCollectionTOC /// @copydetails ShellDocumentTOC /// @copydetails ShellEdgeTOC /// @copydetails SimpleQueriesTOC /// @copydetails AqlTOC /// @copydetails UserManualActionsTOC /// @copydetails CommandLineTOC /// @endif //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- USER MANUAL ARANGOSH // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @page UserManualArangoshTOC /// /// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @page UserManualArangosh The Arango Shell /// /// @EMBEDTOC{UserManualArangoshTOC} /// /// @section UserManualArangoshOutput Arango Shell Output ///////////////////////////////////////////////////////// /// /// In general the Arango Shells prints its as output to standard output channel /// using the JSON stringifier. /// /// @code /// arangosh> db.five.all().toArray(); /// [{ _id : "2223655/3665447", _rev : 3665447, name : "one" }, /// { _id : "2223655/3730983", _rev : 3730983, name : "two" }, /// { _id : "2223655/3862055", _rev : 3862055, name : "four" }, /// { _id : "2223655/3993127", _rev : 3993127, name : "three" }] /// @endcode /// /// @CLEARPAGE /// @FUN{start_pretty_print()} /// /// While the standard JSON stringifier is very concise it is hard to read. /// Calling the function @FN{start_pretty_print} will enable the pretty printer /// which formats the output in a human readable way. /// /// @code /// arangosh> start_pretty_print(); /// using pretty printing /// arangosh> db.five.all().toArray(); /// [ /// { /// _id : "2223655/3665447", /// _rev : 3665447, /// name : "one" /// }, /// { /// _id : "2223655/3730983", /// _rev : 3730983, /// name : "two" /// }, /// { /// _id : "2223655/3862055", /// _rev : 3862055, /// name : "four" /// }, /// { /// _id : "2223655/3993127", /// _rev : 3993127, /// name : "three" /// } /// ] /// @endcode /// /// @CLEARPAGE /// @FUN{stop_pretty_print()} /// /// The functions disable the pretty printer, switching back to the standard /// JSON output format. //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- USER MANUAL ACTIONS // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @page UserManualActionsTOC /// /// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @page UserManualActions Arango Actions /// /// Please note, that user Actions in ArangoDB are still preliminary and details /// are subject to change. /// /// @EMBEDTOC{UserManualActionsTOC} /// /// @section UserManualActionsIntro Introduction to User Actions //////////////////////////////////////////////////////////////// /// /// In some ways the communication layer of the ArangoDB server behaves like a /// Web server. Unlike a Web server, it normally responds to HTTP requests by /// delivering JSON objects. Remember, documents in the database are just JSON /// objects. So, most of the time the HTTP response will contain a JSON document /// from the database as body. You can extract the documents stored in the /// database using HTTP @LIT{GET}. You can store documents using HTTP /// @LIT{POST}. /// /// However, there is something more. You can write small sniplets - so called /// actions - to extend the database. The idea of actions is that sometimes it /// is better to store parts of the business logic within AnrangoDB. /// /// The simplest example is the age of a person. Assume you store information /// about people in your database. It is an anti-pattern to store the age, /// because it changes every now and then. Therefore, you normally store the /// birthday and let the client decide what to do with it. However, if you have /// many different clients, it might be easier to enrich the person document /// with the age using actions once on the server side. /// /// Or, for instance, if you want to apply some statistics to large data-sets /// and you cannot easily express this as query. You can define a action instead /// of transferring the whole data to the client and do the computation on the /// client. /// /// Actions are also useful if you want to restrict and filter data according to /// some complex permission system. /// /// The ArangoDB server can deliver all kinds of information, JSON being only /// one possible format. You can also generate HTML or images. However, a Web /// server is normally better suited for the task as it also implements various /// caching strategies, language selection, compression and so on. Having said /// that, there are still situations where it might be suitable to use the /// ArangoDB to deliver HTML pages - static or dynamic. An simple example is the /// built-in administration interface. You can access it using any modern /// browser and there is no need for a separate Apache or IIS. /// /// The following sections will explain actions within ArangoDB and show how to /// define them. The examples start with delivering static HTML pages - even if /// this is not the primary use-case for actions. The later sections will then /// show you how to code some pieces of your business logic and return JSON /// objects. /// /// The interface is loosely modelled after the JavaScript classes for HTTP /// request and responses found in node.js and the middleware/routing aspects /// of connect.js and express.js. /// /// Note that unlike node.js, ArangoDB is multi-threaded and there is no easy /// way to share state between queries inside the JavaScript engine. If such /// state information is required, you need to use the database itself. /// /// @section UserManualActionsHelloWorld A Hello World Example ////////////////////////////////////////////////////////////// /// /// The client API or browser sends a HTTP request to the ArangoDB server and /// the server returns a HTTP response to the client. A HTTP requests consists /// of a method, normally @LIT{GET} or @LIT{POST} when using a browser, and a /// request path like @LIT{/hello/world}. For a real Web server there are a zillion /// of other thing to consider, we will ignore this for the moment. The HTTP /// response contains a content type, describing how to interpret the returned /// data, and the data itself. /// /// In the following example, we want to define an action in ArangoDB, so that the /// server returns the HTML document /// /// @code /// /// /// Hello World /// /// /// @endcode /// /// if asked @LIT{GET /hello/world}. /// /// The server needs to know what function to call or what document to deliver /// if it receives a request. This is called routing. All the routing information /// of ArangoDB is stored in a collection @LIT{_routing}. Each entry in this /// collections describes how to deal with a particular request path. /// /// For the above example, add the following document to the @{_routing} /// collection: /// /// @code /// arangosh> db._routing.save({ /// ........> url: { match: "/hello/world" }, /// ........> content: { /// ........> contentType: "text/html", /// ........> body: "Hello World" }}); /// @endcode /// /// In order to activate the new routing, you must either restart the server /// or call the internal reload function. /// /// @code /// arangosh> require("internal").reloadRouting() /// @endcode /// /// Now use the browser and access /// /// @LIT{http://localhost:8529/hello/world} /// /// You should see the @LIT{Hello World} in our browser. /// /// @section UserManualActionsMatches Matching an URL ///////////////////////////////////////////////////// /// /// There are a lot of options for the @LIT{url} attribute. If you define /// different routing for the same path, then the following simple rule is /// applied in order to determine which match wins: If there are two matches, /// then the more specific wins. I. e, if there is a wildcard match and an exact /// match, the exact match is prefered. If there is a short and a long match, /// the longer match wins. /// /// @subsection UserManualActionsMatchesExact Exact Match /// /// If the definition is /// /// @code /// { url: { match: "/hello/world" } } /// @endcode /// /// then the match must be exact. Only the request for @LIT{/hello/world} will /// match, everything else, e. g. @LIT{/hello/world/my} or @LIT{/hello/world2}, /// will not match. /// /// The following definition is a short-cut for an exact match. /// /// @code /// { url: "/hello/world" } /// @endcode /// /// @subsection UserManualActionsMatchesPrefix Prefix Match /// /// If the definition is /// /// @code /// { url: { match: "/hello/world/*" } } /// @endcode /// /// then the match can be a prefix match. The requests for @LIT{/hello/world}, /// @LIT{/hello/world/my}, and @LIT{/hello/world/how/are/you} will all /// match. However @LIT{/hello/world2} does not match. Prefix matches within an /// URL part, i. e. @LIT{/hello/world*}, are not allowed. The wildcard must /// occur at the end, i. e. /// /// @code /// /hello/*/world /// @endcode /// /// is also disallowed. /// /// If you define two routes /// /// @code /// { url: { match: "/hello/world/*" } } /// { url: { match: "/hello/world/emil" } } /// @endcode /// /// then the second route will be used for @LIT{/hello/world/emil} because it is /// more specific. /// /// @subsection UserManualActionsMatchesParameterized Parameterized Match /// /// A parameterized match is similar to a prefix match, but the parameters are /// also allowed inside the URL path. /// /// If the definition is /// /// @code /// { url: { match: "/hello/:name/world" } } /// @endcode /// /// then the URL must have three parts, the first part being @LIT{hello} and the /// third part @LIT{world}. For example, @LIT{/hello/emil/world} will match, /// while @LIT{/hello/emil/meyer/world} will not. /// /// @subsection UserManualActionsMatchesConstraint Constraint Match /// /// A constraint match is similar to a parameterized match, but the parameters /// can carry constraints. /// /// If the definition is /// /// @code /// { url: { match: "/hello/:name/world", constraint: { name: "/[a-z]+/" } } /// @endcode /// /// then the URL must have three parts, the first part being @LIT{hello} and the /// third part @LIT{world}. The second part must be all lowercase. /// /// It is possible to use more then one constraint for the same URL part. /// /// @code /// { url: { match: "/hello/:name|:id/world", /// constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } } /// @endcode /// /// @subsection UserManualActionsMatchesOptional Optional Match /// /// An optional match is similar to a parameterized match, but the last /// parameter is optional. /// /// If the definition is /// /// @code /// { url: { match: "/hello/:name?", constraint: { name: "/[a-z]+/" } } /// @endcode /// /// then the URL @LIT{/hello} and @LIT{/hello/emil} will match. /// /// If the definitions are /// /// @code /// { url: { match: "/hello/world" } } /// { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } /// { url: { match: "/hello/*" } } /// @endcode /// /// then the URL @LIT{/hello/world} will be matched by the first route, because it /// is the most specific. The URL @LIT{/hello/you} will be matched by the second /// route, because it is more specific than the prefix match. /// /// @subsection UserManualActionsMatchesMethod Method Restriction /// /// You can restrict the match to specific methods. /// /// If the definition is /// /// @code /// { url: { match: "/hello/world", methods: [ "post", "put" ] } /// @endcode /// /// then only @LIT{POST} and @LIT{PUT} requests will match. /// /// @subsection UserManualActionsMatching More on Matching /// /// Remember that the more specific match wins. /// /// - A match without parameter or wildcard is more specific than a match with /// parameters or wildcard. /// - A match with parameter is more specific than a match with a wildcard. /// - If there is more than one parameter, specificity is applied from left to /// right. /// /// Consider the following definitions /// /// @code /// (1) { url: { match: "/hello/world" } } /// (2) { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } /// (3) { url: { match: "/:something/world" } /// (4) { url: { match: "/hello/*" } } /// @endcode /// /// Then /// /// - @LIT{/hello/world} is match by (1) /// - @LIT{/hello/emil} is match by (2) /// - @LIT{/your/world} is match by (3) /// - @LIT{/hello/you} is match by (4) /// /// You can write the following document into the @LIT{_routing} collection /// to test the above examples. /// /// @code /// { /// routes: [ /// { url: { match: "/hello/world" }, content: "route 1" }, /// { url: { match: "/hello/:name|:id", constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }, content: "route 2" }, /// { url: { match: "/:something/world" }, content: "route 3" }, /// { url: { match: "/hello/*" }, content: "route 4" }, /// ] /// } /// @endcode /// /// @section UserManualActionsHelloJson A Hello World Example for JSON ////////////////////////////////////////////////////////////////////// /// /// If you change the example slightly, then a JSON object will be delivered. /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/hello/json", /// ........> content: { /// ........> contentType: "application/json", /// ........> body: "{ \"hello\" : \"world\" }" }}); /// arangosh> require("internal").reloadRouting() /// @endcode /// /// Again check with your browser /// /// @LIT{http://localhost:8529/hello/json} /// /// Depending on your browser and installed add-ons you will either see the /// JSON object or a download dialog. If your browser wants to open an external /// application to display the JSON object, you can change the @LIT{contentType} /// to @LIT{"text/plain"} for the example. This makes it easier to check the /// example using a browser. Or use @LIT{curl} to access the server. /// /// @code /// bash> curl "http://127.0.0.1:8529/hello/json" && echo /// { "hello" : "world" } /// @endcode /// /// @section UserManualActionsContent Delivering Content //////////////////////////////////////////////////////// /// /// There are a lot of different ways on how to deliver content. We have already /// seen the simplest one, where static content is delivered. The fun, however, /// starts when delivering dynamic content. /// /// @subsection UserManualActionsContentStatic Static Content /// /// You can specify a body and a content-type. /// /// @code /// { content: { /// contentType: "text/html", /// body: "Hallo World" /// } /// } /// @endcode /// /// If the content type is @LIT{text/plain} then you can use the short-cut /// /// @code /// { content: "Hallo World" } /// @endcode /// /// @subsection UserManualActionsContentAction A Simple Action /// /// The simplest dynamic action is: /// /// @code /// { action: { controller: "org/arangodb/actions", do: "echoRequest" } } /// @endcode /// /// It is not possible to store functions directly in the routing table, but you /// can call functions defined in modules. In the above example the function can /// be accessed from JavaScript as: /// /// @LIT{require("org/arangodb/actions").echoRequest} /// /// The function @LIT{echoRequest} is pre-defined. It takes the request objects /// and echos it in the response. /// /// The signature of such a function must be /// /// @code /// function (req, res, options, next) /// @endcode /// /// For example /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/hello/echo", /// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } }); /// @endcode /// /// Reload the routing and check /// /// @LIT{http://127.0.0.1:8529/hello/echo} /// /// You should see something like /// /// @code /// { /// "request": { /// "path": "/hello/echo", /// "headers": { /// "accept-encoding": "gzip, deflate", /// "accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3", /// "connection": "keep-alive", /// "content-length": "0", /// "host": "localhost:8529", /// "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0" /// }, /// "requestType": "GET", /// "parameters": { } /// }, /// "options": { } /// } /// @endcode /// /// The request might contain @LIT{path}, @LIT{prefix}, @LIT{suffix}, and /// @LIT{urlParameters} attributes. @LIT{path} is the complete path as supplied /// by the user and always available. If a prefix was matched, then this prefix /// is stored in the attribute @LIT{prefix} and the remaining URL parts are /// stored as an array in @LIT{suffix}. If one or more parameters were matched, /// then the parameter values are stored in @LIT{urlParameters}. /// /// For example, if the url description is /// /// @code /// { url: { match: "/hello/:name/:action" } } /// @endcode /// /// and you request the path @LIT{/hello/emil/jump}, then the request object /// will contain the following attribute /// /// @code /// urlParameters: { name: "emil", action: "jump" } } /// @endcode /// /// @subsection UserManualActionsContentController Action Controller /// /// As an alternative to the simple action, you can use controllers. A /// controller is a module, defines the function @LIT{get}, @LIT{put}, /// @LIT{post}, @LIT{delete}, @LIT{head}, @LIT{patch}. If a request of /// the corresponding type is matched, the function will be called. /// /// For example /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/hello/echo", /// ........> action: { controller: "org/arangodb/actions/echoController" } }); /// @endcode /// /// @subsection UserManualActionsContentPrefix Prefix Action Controller /// /// The controller is selected when the definition is read. There is a /// more flexible, but slower and maybe insecure variant, the prefix /// controller. /// /// Assume that the url is a prefix match /// /// @code /// { url: { match: /hello/*" } } /// @endcode /// /// You can use /// /// @code /// { action: { prefixController: "org/arangodb/actions" } } /// @endcode /// /// to define a prefix controller. If the URL @LIT{/hello/echoController} is /// given, then the module @LIT{org/arangodb/actions/echoController} is used. /// /// If you use an prefix controller, you should make certain that no unwanted /// actions are available under the prefix. /// /// The definition /// /// @code /// { action: "org/arangodb/actions" } /// @endcode /// /// is a short-cut for a prefix controller definition. /// /// @section UserManualActionsReqRes Requests and Responses /////////////////////////////////////////////////////////// /// /// The controller must define handler functions which take a request object and /// fill the response object. /// /// A very simple example is the function @LIT{echoRequest} defined in /// the module @LIT{org/arangodb/actions}. /// /// @code /// function (req, res, options, next) { /// var result; /// /// result = { request: req, options: options }; /// /// res.responseCode = exports.HTTP_OK; /// res.contentType = "application/json"; /// res.body = JSON.stringify(result); /// } /// @endcode /// /// Install it as /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/echo", /// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } }); /// @endcode /// /// Reload the routing and check /// /// @LIT{http://127.0.0.1:8529/hello/echo} /// /// You should see something like /// /// @code /// { /// "request": { /// "prefix": "/hello/echo", /// "suffix": [ /// "hello", /// "echo" /// ], /// "path": "/hello/echo", /// "headers": { /// "accept-encoding": "gzip, deflate", /// "accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3", /// "connection": "keep-alive", /// "content-length": "0", /// "host": "localhost:8529", /// "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0" /// }, /// "requestType": "GET", /// "parameters": { } /// }, /// "options": { } /// } /// @endcode /// /// You may also pass options to the called function: /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/echo", /// ........> action: { /// ........> controller: "org/arangodb/actions", /// ........> do: "echoRequest", /// ........> options: { "Hallo": "World" } } }); /// @endcode /// /// You should now see the options in the result. /// /// @code /// { /// "request": { /// ... /// }, /// "options": { /// "Hallo": "world" /// } /// } /// @endcode /// /// @section UserManualActionsModify Modifying Request and Response /// /// As we've seen in the previous examples, actions get called /// with the request and response objects (named @LIT{req} and @LIT{res} in the /// examples) passed as parameters to their handler functions. /// /// The @LIT{req} object contains the incoming HTTP request, which /// might or might not have been modified by a previous action (if actions were /// chained). /// /// A handler can modify the request object in place if desired. This might /// be useful when writing middleware (see below) that is used to intercept /// incoming requests, modify them and pass them to the actual handlers. /// /// While modifying the request object might not be that relevant for /// non-middleware actions, modifying the response object definitely is. Modifying /// the response object is an action's only way to return data to the caller of /// the action. /// /// We've already seen how to set the HTTP status code, the content type, and the /// result body. The @LIT{res} object has the following properties for these: /// - contentType: MIME type of the body as defined in the HTTP standard (e.g. /// @LIT{text/html}, @LIT{text/plain}, @LIT{application/json}, ...) /// - responsecode: the HTTP status code of the response as defined in the HTTP /// standard. Common values for actions that succeed are @LIT{200} or @LIT{201}. /// Please refer to the HTTP standard for more information. /// - body: the actual response data /// /// To set or modify arbitrary headers of the response object, the @LIT{headers} /// property can be used. For example, to add a user-defined header to the response, /// the following code will do: /// /// @code /// res.headers = res.headers || { }; // headers might or might not be present /// res.headers['X-Test'] = 'someValue'; // set header X-Test to "someValue" /// @endcode /// /// This will set the additional HTTP header @LIT{X-Test} to value @LIT{someValue}. /// Other headers can be set as well. Note that ArangoDB might change the case /// of the header names to lower case when assembling the overall response that /// is sent to the caller. /// /// It is not necessary to explicitly set a @LIT{Content-Length} header for the /// response as ArangoDB will calculate the content length automatically and add /// this header itself. ArangoDB might also add a @LIT{Connection} header itself /// to handle HTTP keep-alive. /// /// ArangoDB also supports automatic transformation of the body data to another /// format. Currently, the only supported transformations are base64-encoding and /// base64-decoding. Using the transformations, an action can create a base64 /// encoded body and still let ArangoDB send the non-encoded version, for example: /// /// @code /// res.body = 'VGhpcyBpcyBhIHRlc3Q='; /// res.transformations = res.transformations || [ ]; // initialise /// res.transformations.push('base64decode'); // will base64 decode the response body /// @endcode /// /// When ArangoDB processes the response, it will base64-decode what's in @LIT{res.body} /// and set the HTTP header @LIT{Content-Encoding: binary}. The opposite can be /// achieved with the @LIT{base64encode} transformation: ArangoDB will then automatically /// base64-encode the body and set a @LIT{Content-Encoding: base64} HTTP header. /// /// @section UserManualActionsHandlers Writing dynamic action handlers ///////////////////////////////////////////////////////////////////// /// /// To write your own dynamic action handlers, you must put them into modules. /// /// Modules are a means of organising action handlers and making them loadable /// under specific names. /// /// To start, we'll define a simple action handler in a module @LIT{/own/test}: /// /// @code /// arangosh> db._modules.save({ /// ........> path: "/own/test", /// ........> content: "exports.do = function(req, res, options, next) { res.body = 'test'; res.responseCode = 200; res.contentType = 'text/html'; };", /// ........> autoload: true }); /// @endcode /// /// This does nothing but register a do action handler in a module @LIT{/own/test}. /// The action handler is not yet callable, but must be mapped to a route first. /// To map the action to the route @LIT{/ourtest}, execute the following command: /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/ourtest", /// ........> action: { controller: "/own/test" } }); /// @endcode /// /// In order to see the module in action, you must either restart the server /// or call the internal reload function. /// /// @code /// arangosh> require("internal").reloadRouting() /// @endcode /// /// Now use the browser and access /// /// @LIT{http://localhost:8529/ourtest} /// /// You will see that the module's do function has been executed. /// /// @section UserManualActionsAdvanced Advanced Usages ////////////////////////////////////////////////////// /// /// For detailed information see the reference manual. /// /// @subsection UserManualActionsAdvancedRedirects Redirects /// /// Use the following for a permanent redirect: /// /// @code /// arangosh> db._routing.save({ /// ........> url: "/", /// ........> action: { /// ........> controller: "org/arangodb/actions", /// ........> do: "redirectRequest", /// ........> options: { /// ........> permanently: true, /// ........> destination: "http://somewhere.else/" } } }); /// @endcode /// /// @subsection UserManualActionsAdvancedBundles Routing Bundles /// /// Instead of adding all routes for package separately, you can /// specify a bundle. /// /// @code /// { /// routes: [ /// { url: "/url1", content: "..." }, /// { url: "/url2", content: "..." }, /// { url: "/url3", content: "..." }, /// ... /// ] /// } /// @endcode /// /// The advantage is, that you can put all your routes into one document /// and use a common prefix. /// /// @code /// { /// urlPrefix: "/test", /// /// routes: [ /// { url: "/url1", content: "..." }, /// { url: "/url2", content: "..." }, /// { url: "/url3", content: "..." }, /// ... /// ] /// } /// @endcode /// /// will define the URL @LIT{/test/url1}, @LIT{/test/url2}, and /// @LIT{/test/url3}. /// /// @subsection UserManualActionsAdvancedMiddleware Writing Middleware /// /// Assume, you want to log every request. In this case you can easily define /// an action for the whole url-space @LIT{/}. This action simply logs /// the requests, calls the next in line, and logs the response. /// /// @code /// exports.logRequest = function (req, res, options, next) { /// console.log("received request: %s", JSON.stringify(req)); /// next(); /// console.log("produced response: %s", JSON.stringify(res)); /// }; /// @endcode /// /// This function is available as @LIT{org/arangodb/actions/logRequest}. /// You need to tell ArangoDB that it is should use a prefix match and /// that the shortest match should win in this case: /// /// @code /// arangosh> db._routing.save({ /// ........> middleware: [ /// ........> { url: { match: "/*" }, action: { controller: "org/arangodb/actions", do: "logRequest" } } /// ........> ] /// ........> }); /// @endcode //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: c++ // mode: outline-minor // outline-regexp: "\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @section\\|/// @subsection\\|/// @\\}\\)" // End: