!SECTION Modifying Request and Response As we've seen in the previous examples, actions get called with the request and response objects (named *req* and *res* in the examples) passed as parameters to their handler functions. The *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 *res* object has the following properties for these: - *contentType*: MIME type of the body as defined in the HTTP standard (e.g. *text/html*, *text/plain*, *application/json*, ...) - *responsecode*: the HTTP status code of the response as defined in the HTTP standard. Common values for actions that succeed are *200* or *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 *headers* property can be used. For example, to add a user-defined header to the response, the following code will do: ```js res.headers = res.headers || { }; // headers might or might not be present res.headers['X-Test'] = 'someValue'; // set header X-Test to "someValue" ``` This will set the additional HTTP header *X-Test* to value *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 *Content-Length* header for the response as ArangoDB will calculate the content length automatically and add this header itself. ArangoDB might also add a *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: ```js res.body = 'VGhpcyBpcyBhIHRlc3Q='; res.transformations = res.transformations || [ ]; // initialise res.transformations.push('base64decode'); // will base64 decode the response body ``` When ArangoDB processes the response, it will base64-decode what's in *res.body* and set the HTTP header *Content-Encoding: binary*. The opposite can be achieved with the *base64encode* transformation: ArangoDB will then automatically base64-encode the body and set a *Content-Encoding: base64* HTTP header. !SECTION Writing dynamic action handlers To write your own dynamic action handlers, you must put them into modules. Modules are a means of organizing action handlers and making them loadable under specific names. To start, we'll define a simple action handler in a module */own/test*: ```js 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'; };" }); ``` This does nothing but register a do action handler in a module */own/test*. The action handler is not yet callable, but must be mapped to a route first. To map the action to the route */ourtest*, execute the following command: ```js arangosh> db._routing.save({ url: "/ourtest", action: { controller: "/own/test" } }); ``` In order to see the module in action, you must either restart the server or call the internal reload function. ```js arangosh> require("internal").reloadRouting() ``` Now use the browser and access http:// localhost:8529/ourtest You will see that the module's do function has been executed. !SECTION A Word about Caching Sometimes it might seem that your change do not take effect. In this case the culprit could be one of the caches. With dynamic actions there are two caches involved: !SUBSECTION The Routing Cache The routing cache stores the routing information computed from the *_routing* collection. Whenever you change this collection manually, you need to call ```js arangosh> require("internal").reloadRouting(); ``` in order to rebuild the cache. !SUBSECTION The Modules Cache If you use a dynamic action and this action is stored in module, then the module functions are also stored in a cache in order to avoid parsing the JavaScript code again and again. Whenever you change the *modules* collections manually, you need to call ```js arangosh> require("internal").flushServerModules(); ``` in order to rebuild the cache. !SUBSECTION Flush Order If you define a dynamic routing and the controller, then you need to flush the caches in a particular order. In order to build the routes, the module information must be known. Therefore, you need to flush the modules caches first. ```js arangosh> require("internal").flushServerModules(); arangosh> require("internal").reloadRouting(); ``` !SECTION Advanced Usages For detailed information see the reference manual. !SUBSECTION Redirects Use the following for a permanent redirect: ```js arangosh> db._routing.save({ url: "/", action: { do: "org/arangodb/actions/redirectRequest", options: { permanently: true, destination: "http://somewhere.else/" } } }); ``` !SUBSECTION Routing Bundles Instead of adding all routes for package separately, you can specify a bundle. ```js { routes: [ { url: "/url1", content: "..." }, { url: "/url2", content: "..." }, { url: "/url3", content: "..." }, ... ] } ``` The advantage is, that you can put all your routes into one document and use a common prefix. ```js { urlPrefix: "/test", routes: [ { url: "/url1", content: "..." }, { url: "/url2", content: "..." }, { url: "/url3", content: "..." }, ... ] } ``` will define the URL */test/url1*, */test/url2*, and */test/url3*. !SUBSECTION Writing Middleware Assume, you want to log every request. In this case you can easily define an action for the whole url-space */*. This action simply logs the requests, calls the next in line, and logs the response. ```js exports.logRequest = function (req, res, options, next) { console.log("received request: %s", JSON.stringify(req)); next(); console.log("produced response: %s", JSON.stringify(res)); }; ``` This function is available as *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: ```js arangosh> db._routing.save({ middleware: [ { url: { match: "/*" }, action: { do: "org/arangodb/actions/logRequest" } } ] }); ``` If you call *next()*, the next specific routing will be used for the original URL. Even if you modify the URL in the request object *req*, this will not cause the *next()* to jump to the routing defined for this next URL. If proceeds occurring the origin URL. However, if you use *next(true)*, the routing will stop and request handling is started with the new URL. You must ensure that *next(true)* is never called without modifying the URL in the request object *req*. Otherwise an endless loop will occur. !SECTION Application Deployment Using single routes or [bundles](#routing_bundles) can be become a bit messy in large applications. Kaerus has written a [deployment tool](https://github.com/kaerus/arangodep) in node.js. Note that there is also [Foxx](../Foxx/README.md) for building applications with ArangoDB. !SECTION Common Pitfalls when using Actions !SUBSECTION Caching If you made any changes to the routing but the changes do not have any effect when calling the modified action's URL, you might have been hit by some caching issues. After any modification to the routing or actions, it is thus recommended to make the changes "live" by calling the following functions from within arangosh: ```js arangosh> require("internal").flushServerModules(); arangosh> require("internal").reloadRouting(); ``` You might also be affected by client-side caching. Browsers tend to cache content and also redirection URLs. You might need to clear or disable the browser cache in some cases to see your changes in effect. !SUBSECTION Data types When processing the request data in an action, please be aware that the data type of all URL parameters is *string*. This is because the whole URL is a string and when the individual parts are extracted, they will also be strings. For example, when calling the URL http:// localhost:8529/hello/world?value=5 the parameter *value* will have a value of (string) *5*, not (number) *5*. This might be troublesome if you use JavaScript's *===* operator when checking request parameter values. The same problem occurs with incoming HTTP headers. When sending the following header from a client to ArangoDB X-My-Value: 5 then the header *X-My-Value* will have a value of (string) *5* and not (number) *5*. !SUBSECTION 501 Not Implemented If you defined a URL in the routing and the URL is accessible fine via HTTP *GET* but returns an HTTP 501 (not implemented) for other HTTP methods such as *POST*, *PUT* or *DELETE*, then you might have been hit by some defaults. By default, URLs defined like this (simple string *url* attribute) are accessible via HTTP *GET* and *HEAD* only. To make such URLs accessible via other HTTP methods, extend the URL definition with the *methods* attribute. For example, this definition only allows access via *GET* and *HEAD*: ```js { url: "/hello/world" } ``` whereas this definition allows HTTP *GET*, *POST*, and *PUT*: ```js { url: { match: "/hello/world", methods: [ "get", "post", "put" ] } } ``` The former definition (defining *url* as an object with a *match* attribute) will result in the URL being accessible via all supported HTTP methods (e.g. *GET*, *POST*, *PUT*, *DELETE*, ...), whereas the latter definition (providing a string *url* attribute) will result in the URL being accessible via HTTP *GET* and HTTP *HEAD* only, with all other HTTP methods being disabled. Calling a URL with an unsupported or disabled HTTP method will result in an HTTP 501 error.