1
0
Fork 0
arangodb/Documentation/Books/Users/Foxx/Develop/Controller.mdpp

1425 lines
39 KiB
Plaintext

!CHAPTER Details on Controller
!SUBSECTION Create
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`new Controller(applicationContext, options)`
This creates a new Controller. The first argument is the controller
context available in the variable *applicationContext*. The second one is an
options array with the following attributes:
* *urlPrefix*: All routes you define within will be prefixed with it.
**Examples**
```js
app = new Controller(applicationContext, {
urlPrefix: "/meadow"
});
```
!SECTION HTTP Methods
!SUBSECTION get
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.get(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `get`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.get('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SUBSECTION head
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.head(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `head`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.head('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SUBSECTION post
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.post(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `post`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.post('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SUBSECTION put
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.put(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `put`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.put('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SUBSECTION patch
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.patch(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `patch`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.patch('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SUBSECTION delete
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.delete(path, callback)`
Defines a new route on `path` that handles requests from the HTTP verb `delete`.
This route can also be 'parameterized' like `/goose/:barn`.
In this case you can later get the value the user provided for `barn`
via the `params` function in the `request`.
The function defined in `callback` will be invoked whenever this type of
request is recieved.
`callback` get's two arguments `request` and `response`, see below for further
information about these objects.
**Examples**
```js
app.delete('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
```
!SECTION Documenting and constraining a specific route
If you now want to document your route, you can use JSDoc style comments (a
multi-line comment block where the first line starts with */*** instead
of */**) above your routes to do that:
```js
/** Get all foxxes
*
* If you want to get all foxxes, please use this
* method to do that.
*/
app.get("/foxxes", function () {
// ...
});
```
The first line will be treated as a summary (For optical reasons in the
produced documentation, the summary is restricted to 60 characters). All
following lines will be treated as additional notes shown in the detailed
view of the route documentation. With the provided information, Foxx will
generate a nice documentation for you. Furthermore you can describe your
API by chaining the following methods onto your path definition:
!SUBSECTION pathParam
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.pathParam(id, options)`
If you defined a route "/foxx/:name", containing a parameter called `name` you can
constrain which format this parameter is allowed to have.
This format is defined using *joi* in the `options` parameter.
Using this function will at first allow you to access this parameter in your
route handler using `req.params(id)`, will reject any request having a paramter
that does not match the *joi* definition and creates a documentation for this
parameter in ArangoDBs WebInterface.
For more information on *joi* see [the official Joi documentation](https://github.com/spumko/joi).
*Parameter*
* *id*: name of the param.
* *options*: a joi schema or an object with the following properties:
* *type*: a joi schema.
* *description*: documentation description for the parameter.
* *required* (optional): whether the parameter is required. Default: determined by *type*.
*Examples*
```js
app.get("/foxx/:name", function {
// Do something
}).pathParam("name", joi.string().required().description("Name of the Foxx"));
```
You can also pass in a configuration object instead:
```js
app.get("/foxx/:name", function {
// Do something
}).pathParam("name", {
type: joi.string(),
required: true,
description: "Name of the Foxx"
});
```
!SUBSECTION queryParam
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.queryParam(id, options)`
Describe a query parameter:
If you defined a route "/foxx", you can allow a query paramter with the
name `id` on it and constrain the format of this parameter by giving it a *joi* type in the `options` parameter.
Using this function will at first allow you to access this parameter in your
route handler using `req.params(id)`, will reject any request having a paramter
that does not match the *joi* definition and creates a documentation for this
parameter in ArangoDBs WebInterface.
For more information on *joi* see [the official Joi documentation](https://github.com/spumko/joi).
You can also provide a description of this parameter and
whether you can provide the parameter multiple times.
*Parameter*
* *id*: name of the parameter
* *options*: a joi schema or an object with the following properties:
* *type*: a joi schema
* *description*: documentation description for this param.
* *required* (optional): whether the param is required. Default: determined by *type*.
* *allowMultiple* (optional): whether the param can be specified more than once. Default: `false`.
*Examples*
```js
app.get("/foxx", function {
// Do something
}).queryParam("id",
joi.string()
.required()
.description("Id of the Foxx")
.meta({allowMultiple: false})
});
```
You can also pass in a configuration object instead:
```js
app.get("/foxx", function {
// Do something
}).queryParam("id", {
type: joi.string().required().description("Id of the Foxx"),
allowMultiple: false
});
```
!SUBSECTION bodyParam
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.bodyParam(paramName, options)`
Defines that this route expects a JSON body when requested and binds it to
a pseudo parameter with the name `paramName`.
The body can than be read in the the handler using `req.params(paramName)` on the request object.
In the `options` parameter you can define how a valid request body should look like.
This definition can be done in two ways, either using *joi* directly.
Accessing the body in this case will give you a JSON object.
The other way is to use a Foxx *Model*.
Accessing the body in this case will give you an instance of this Model.
For both ways an entry for the body will be added in the Documentation in ArangoDBs WebInterface.
For information about how to annotate your models, see the Model section.
All requests sending a body that does not match the validation given this way
will automatically be rejected.
You can also wrap the definition into an array, in this case this route
expects a body of type array containing arbitrary many valid objects.
Accessing the body parameter will then of course return an array of objects.
Note: The behavior of `bodyParam` changes depending on the `rootElement` option
set in the [manifest](../Develop/Manifest.md). If it is set to `true`, it is
expected that the body is an
object with a key of the same name as the `paramName` argument.
The value of this object is either a single object or in the case of a multi
element an array of objects.
*Parameter*
* *paramName*: name of the body parameter in `req.parameters`.
* *options*: a joi schema or an object with the following properties:
* *description*: the documentation description of the request body.
* *type*: the Foxx model or joi schema to use.
* *allowInvalid* (optional): `true` if validation should be skipped. (Default: `false`)
*Examples*
```js
app.post("/foxx", function (req, res) {
var foxxBody = req.parameters.foxxBody;
// Do something with foxxBody
}).bodyParam("foxxBody", {
description: "Body of the Foxx",
type: FoxxBodyModel
});
```
Using a joi schema:
```js
app.post("/foxx", function (req, res) {
var joiBody = req.parameters.joiBody;
// Do something with the number
}).bodyParam("joiBody", {
type: joi.number().integer().min(5),
description: "A number greater than five",
allowInvalid: false // default
});
```
Shorthand version:
```js
app.post("/foxx", function (req, res) {
var joiBody = req.parameters.joiBody;
// Do something with the number
}).bodyParam(
"joiBody",
joi.number().integer().min(5)
.description("A number greater than five")
.meta({allowInvalid: false}) // default
);
```
!SUBSECTION errorResponse
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.errorResponse(errorClassOrName, code, description, [callback])`
Define a reaction to a thrown error for this route: If your handler throws an error
of the errorClass defined in `errorClassOrName` or the error has an attribute `name` equal to `errorClassOrName`,
it will be caught and the response object will be filled with the given
status code and a JSON with error set to your description as the body.
If you want more control over the returned JSON, you can give an optional fourth
parameter in form of a function. It gets the error as an argument, the return
value will be transformed into JSON and then be used as the body.
The status code will be used as described above. The description will be used for
the documentation.
It also adds documentation for this error response to the generated documentation.
*Examples*
```js
/* define our own error type, FoxxyError */
var FoxxyError = function (message) {
this.name = "FError";
this.message = "the following FoxxyError occurred: " + message;
};
FoxxyError.prototype = new Error();
app.get("/foxx", function {
/* throws a FoxxyError */
throw new FoxxyError();
}).errorResponse(FoxxyError, 303, "This went completely wrong. Sorry!");
app.get("/foxx", function {
throw new FoxxyError("oops!");
}).errorResponse("FError", 303, "This went completely wrong. Sorry!", function (e) {
return {
code: 123,
desc: e.message
};
});
```
!SUBSECTION onlyif
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.onlyIf(check)`
This functionality is used to secure a route by applying a checking function
on the request beforehand, for example the check authorization.
It expects `check` to be a function that takes the request object as first parameter.
This function is executed before the actual handler is invoked.
If `check` throws an error the actual handler will not be invoked.
Remember to provide an `errorResponse` on the route as well to define the behavior in this case.
*Examples*
```js
app.get("/foxx", function {
// Do something
}).onlyIf(aFunction).errorResponse(ErrorClass, 303, "This went completely wrong. Sorry!");
```
!SUBSECTION onlyIfAuthenticated
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`FoxxController#onlyIfAuthenticated(code, reason)`
Please activate sessions for this app if you want to use this function.
Or activate authentication (deprecated).
If the user is logged in, it will do nothing. Otherwise it will respond with
the status code and the reason you provided (the route handler won't be called).
This will also add the according documentation for this route.
*Examples*
```js
app.get("/foxx", function {
// Do something
}).onlyIfAuthenticated(401, "You need to be authenticated");
```
!SUBSECTION summary
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.summary(description)`
Set the summary for this route in the documentation.
Can't be longer than 8192 characters.
This is equal to using JavaDoc style comments right above your function.
If you provide both comment and `summary()` the call to `summary()` wins
and will be used.
*Examples*
Version with comment:
```js
/** Short description
*
* Longer description
* with multiple lines
*/
app.get("/foxx", function() {
});
```
is identical to:
```js
app.get("/foxx", function() {
})
.summary("Short description")
.notes(["Longer description", "with multiple lines"]);
```
!SUBSECTION notes
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Route.notes(...description)`
Set the long description for this route in the documentation
*Examples*
Version with comment:
```js
/** Short description
*
* Longer description
* with multiple lines
*/
app.get("/foxx", function() {
});
```
is identical to:
```js
app.get("/foxx", function() {
})
.summary("Short description")
.notes(["Longer description", "with multiple lines"]);
```
!SUBSECTION extend
In many use-cases several of the functions are always used in a certain combination (e.g.: `onlyIf` with `errorResponse`).
In order to avoid duplicating this equal usage for several routes in your application you can
extend the controller with your own functions.
These functions can simply combine several of the above on a single name, so you only have to
invoke your self defined single function on all routes using these extensions.
`Controller.extend(extensions)`
Extends all functions to define routes in this controller.
This allows to combine several route extensions with the invocation
of a single function.
This is especially useful if you use the same input parameter in several routes of
your controller and want to apply the same validation, documentation and error handling
for it.
The `extensions` parameter is a JSON object with arbitrary keys.
Each key is used as the name of the function you want to define (you cannot overwrite
internal functions like `pathParam`) and the value is a function that will be invoked.
This function can get arbitrary many arguments and the `this` of the function is bound
to the route definition object (e.g. you can use `this.pathParam()`).
Your newly defined function is chainable similar to the internal functions.
**Examples**
Define a validator for a queryParameter, including documentation and errorResponses
in a single command:
```js
controller.extend({
myParam: function (maxValue) {
this.queryParam("value", {type: joi.number().required()});
this.onlyIf(function(req) {
var v = req.param("value");
if (v > maxValue) {
throw new NumberTooLargeError();
}
});
this.errorResponse(NumberTooLargeError, 400, "The given value is too large");
}
});
controller.get("/goose/barn", function(req, res) {
// Will only be invoked if the request has parameter value and it is less or equal 5.
}).myParam(5);
```
!SECTION Documenting and constraining all routes
In addition to documenting a specific route, you can also
do the same for all routes of a controller. For this purpose
use the **allRoutes** object of the according controller.
The following methods are available.
**Examples**
Provide an error response for all routes handled by this controller:
```js
ctrl.allRoutes
.errorResponse(Unauthorized, 401, 'Not authenticated.')
.errorResponse(NotFound, 404, 'Document not found.')
.errorResponse(ImATeapot, 418, 'I\'m a teapot.');
ctrl.get('/some/route', function (req, res) {
// ...
throw new NotFound('The document does not exist');
// ...
}); // no errorResponse needed here
ctrl.get('/another/route', function (req, res) {
// ...
throw new NotFound('I made you a cookie but I ated it');
// ...
}); // no errorResponse needed here either
```
!SUBSECTION errorResponse
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Controller.allRoutes.errorResponse(errorClass, code, description)`
This is equal to invoking `Route.errorResponse` on all routes bound to this controller.
*Examples*
```js
app.allRoutes.errorResponse(FoxxyError, 303, "This went completely wrong. Sorry!");
app.get("/foxx", function {
// Do something
});
```
!SUBSECTION onlyIf
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Controller.allRoutes.onlyIf(code, reason)`
This is equal to invoking `Route.onlyIf` on all routes bound to this controller.
*Examples*
```js
app.allRoutes.onlyIf(myPersonalCheck);
app.get("/foxx", function {
// Do something
});
```
!SUBSECTION onlyIfAuthenticated
<!-- js/server/modules/@arangodb/foxx/request_context.js -->
`Controller.allRoutes.onlyIfAuthenticated(code, description)`
This is equal to invoking `Route.onlyIfAuthenticated` on all routes bound to this controller.
*Examples*
```js
app.allRoutes.onlyIfAuthenticated(401, "You need to be authenticated");
app.get("/foxx", function {
// Do something
});
```
!SUBSECTION pathParam
`Controller.allRoutes.pathParam(id, options)`
This is equal to invoking `Route.pathParam` on all routes bound to this controller.
*Examples*
```js
app.allRoutes.pathParam("id", joi.string().required().description("Id of the Foxx"));
app.get("/foxx/:id", function {
// Secured by pathParam
});
```
You can also pass in a configuration object instead:
```js
app.allRoutes.pathParam("id", {
type: joi.string(),
required: true,
description: "Id of the Foxx"
});
app.get("/foxx/:id", function {
// Secured by pathParam
});
```
!SUBSECTION bodyParam
`Controller.allRoutes.queryParam(id, options)`
This is equal to invoking `Route.queryParam` on all routes bound to this controller.
*Examples*
```js
app.allroutes.queryParam("id",
joi.string()
.required()
.description("Id of the Foxx")
.meta({allowMultiple: false})
});
app.get("/foxx", function {
// Do something
});
```
You can also pass in a configuration object instead:
```js
app.allroutes.queryParam("id", {
type: joi.string().required().description("Id of the Foxx"),
allowMultiple: false
});
app.get("/foxx", function {
// Do something
});
```
!SECTION Before and After Hooks
You can use the following two functions to do something before or respectively
after the normal routing process is happening. You could use that for logging
or to manipulate the request or response (translate it to a certain format for
example).
!SUBSECTION before
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.before(path, callback)`
Defines an additional function on the route `path` which will be executed
before the callback defined for a specific HTTP verb is executed.
The `callback` function has the same signature as the `callback` in the
specific route.
You can also omit the `path`, in this case `callback` will be executed
before handleing any request in this Controller.
If `callback` returns the Boolean value `false`, the route handling
will not proceed. You can use this to intercept invalid or unauthorized
requests and prevent them from being passed to the matching routes.
**Examples**
```js
app.before('/high/way', function(req, res) {
//Do some crazy request logging
});
```
!SUBSECTION after
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.after(path, callback)`
Similar to `Controller.before(path, callback)` but `callback` will be invoked
after the request is handled in the specific route.
**Examples**
```js
app.after('/high/way', function(req, res) {
//Do some crazy response logging
});
```
!SUBSECTION around
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.around(path, callback)`
Similar to `Controller.before(path, callback)` `callback` will be invoked
instead of the specific handler.
`callback` takes two additional paramaters `opts` and `next` where
`opts` contains options assigned to the route and `next` is a function.
Whenever you call `next` in `callback` the specific handler is invoked,
if you do not call `next` the specific handler will not be invoked at all.
So using around you can execute code before and after a specific handler
and even call the handler only under certain circumstances.
If you omit `path` `callback` will be called on every request.
**Examples**
```js
app.around('/high/way', function(req, res, opts, next) {
//Do some crazy request logging
next();
//Do some more crazy request logging
});
```
!SECTION The Request and Response Objects
When you have created your FoxxController you can now define routes on it.
You provide each with a function that will handle the request. It gets two
arguments (four, to be honest. But the other two are not relevant for now):
* The **request** object
* The **response** object
These objects are provided by the underlying ArangoDB actions and enhanced
by the **BaseMiddleware** provided by Foxx.
!SUBSECTION The Request Object
The **request** object inherits several attributes from the underlying Actions:
* **compatibility**: an integer specifying the compatibility version sent by the
client (in request header **x-arango-version**). If the client does not send this
header, ArangoDB will set this to the minimum compatible version number. The
value is 10000 * major + 100 * minor (e.g. *10400* for ArangoDB version 1.4).
* *user*: the name of the current ArangoDB user. This will be populated only
if authentication is turned on, and will be *null* otherwise.
* *database*: the name of the current database (e.g. *_system*)
* *protocol*: *http* or *https*
* *server*: a JSON object with sub-attributes *address* (containing server
host name or IP address) and *port* (server port).
* *path*: request URI path, with potential [database name](../../Glossary/README.md#database-name) stripped off.
* *url*: request URI path + query string, with potential database name
stripped off
* *headers*: a JSON object with the request headers as key/value pairs.
**Note:** All header field names are lower-cased
* *cookies*: a JSON object with the request cookies as key/value pairs
* *requestType*: the request method (e.g. "GET", "POST", "PUT", ...)
* *requestBody*: the complete body of the request as a string
* *parameters*: a JSON object with all parameters set in the URL as key/value
pairs
* *urlParameters*: a JSON object with all named parameters defined for the
route as key/value pairs.
In addition to these attributes, a Foxx request objects provides the following
convenience methods:
!SUBSECTION body
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.body()`
Get the JSON parsed body of the request. If you need the raw version, please
refer to the *rawBody* function.
!SUBSECTION rawBody
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.rawBody()`
The raw request body, not parsed. The body is returned as a UTF-8 string.
Note that this can only be used sensibly if the request body contains
valid UTF-8. If the request body is known to contain non-UTF-8 data, the
request body can be accessed by using `request.rawBodyBuffer`.
!SUBSECTION rawBodyBuffer
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.rawBodyBuffer()`
The raw request body, returned as a Buffer object.
!SUBSECTION params
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.params(key)`
Get the parameters of the request. This process is two-fold:
* If you have defined an URL like */test/:id* and the user requested
*/test/1*, the call *params("id")* will return *1*.
* If you have defined an URL like */test* and the user gives a query
component, the query parameters will also be returned. So for example if
the user requested */test?a=2*, the call *params("a")* will return *2*.
!SUBSECTION cookie
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.cookie(name, cfg)`
Read a cookie from the request. Optionally the cookie's signature can be verified.
*Parameter*
* *name*: the name of the cookie to read from the request.
* *cfg* (optional): an object with any of the following properties:
* *signed* (optional): an object with any of the following properties:
* *secret*: a secret string that was used to sign the cookie.
* *algorithm*: hashing algorithm that was used to sign the cookie. Default: *"sha256"*.
If *signed* is a string, it will be used as the *secret* instead.
If a *secret* is provided, a second cookie with the name *name + ".sig"* will
be read and its value will be verified as the cookie value's signature.
If the cookie is not set or its signature is invalid, "undefined" will be returned instead.
**Examples**
```
var sid = request.cookie("sid", {signed: "keyboardcat"});
```
!SUBSECTION requestParts
Only useful for multi-part requests.
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`request.requestParts()`
Returns an array containing the individual parts of a multi-part request.
Each part contains a `headers` attribute with all headers of the part,
and a `data` attribute with the content of the part in a Buffer object.
If the request is not a multi-part request, this function will throw an
error.
!SECTION The Response Object
Every response object has the body attribute from the underlying Actions
to set the raw body by hand.
You provide your response body as a string here.
!SUBSECTION Response status
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.status(code)`
Set the status *code* of your response, for example:
**Examples**
```
response.status(404);
```
!SUBSECTION Response set
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.set(key, value)`
Set a header attribute, for example:
**Examples**
```js
response.set("Content-Length", 123);
response.set("Content-Type", "text/plain");
```
or alternatively:
```js
response.set({
"Content-Length": "123",
"Content-Type": "text/plain"
});
```
!SUBSECTION Response json
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.json(object)`
Set the content type to JSON and the body to the JSON encoded *object*
you provided.
**Examples**
```js
response.json({'born': 'December 12, 1915'});
```
!SUBSECTION Response cookie
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.cookie(name, value, cfg)`
Add a cookie to the response. Optionally the cookie can be signed.
*Parameter*
* *name*: the name of the cookie to add to the response.
* *value*: the value of the cookie to add to the response.
* *cfg* (optional): an object with any of the following properties:
* *ttl* (optional): the number of seconds until this cookie expires.
* *path* (optional): the cookie path.
* *domain* (optional): the cookie domain.
* *secure* (optional): mark the cookie as safe transport (HTTPS) only.
* *httpOnly* (optional): mark the cookie as HTTP(S) only.
* *signed* (optional): an object with any of the following properties:
* *secret*: a secret string to sign the cookie with.
* *algorithm*: hashing algorithm to sign the cookie with. Default: *"sha256"*.
If *signed* is a string, it will be used as the *secret* instead.
If a *secret* is provided, a second cookie with the name *name + ".sig"* will
be added to the response, containing the cookie's HMAC signature.
**Examples**
```
response.cookie("sid", "abcdef", {signed: "keyboardcat"});
```
!SUBSECTION Response send
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.send(value)`
Sets the response body to the specified *value*. If *value* is a Buffer
object, the content type will be set to `application/octet-stream` if not
yet set. If *value* is a string, the content type will be set to `text/html`
if not yet set. If *value* is an object, it will be treated as in `res.json`.
**Examples**
```js
response.send({"born": "December 12, 1915"});
response.send(new Buffer("some binary data"));
response.send("<html><head><title>Hello World</title></head><body></body></html>");
```
!SUBSECTION Response sendFile
<!-- js/server/modules/@arangodb/foxx/base_middleware.js -->
`response.sendFile(filename, options)`
Sets the content of the specified file as the response body. The filename
must be absolute. If no content type is yet set for the response, the
response's content type will be determined automatically based
on the filename extension. If no content type is known for the extension,
the content type will default to `application/octet-stream`.
The `options` array can be used to control the behavior of sendFile.
Currently only the following option exists:
- `lastModified`: if set to true, the last modification date and time
of the file will be returned in the `Last-Modified` HTTP header
**Examples**
```js
response.sendFile('/tmp/results.json');
response.sendFile(applicationContext.fileName('image.png'), { lastModified: true });
```
!SECTION Controlling Access to Foxx Applications
Access to Foxx applications is controlled by the regular authentication mechanisms
present in ArangoDB. The server can be run with or without HTTP authentication.
If authentication is turned on,
then every access to the server is authenticated via HTTP authentication. This
includes Foxx applications. The global authentication can be toggled
via the configuration option.
If global HTTP authentication is turned on, requests to Foxx applications will
require HTTP authentication too, and only valid users present in the *_users*
system collection are allowed to use the applications.
Since ArangoDB 1.4, there is an extra option to restrict the authentication to
just system API calls, such as */_api/...* and */_admin/...*. This option can be
turned on using the
"server.authenticate-system-only" configuration option. If it is turned on,
then only system API requests need authentication whereas all requests to Foxx
applications and routes will not require authentication.
This is recommended if you want to disable HTTP authentication for Foxx applications
but still want the general database APIs to be protected with HTTP authentication.
If you need more fine grained control over the access to your Foxx application,
we built an authentication system you can use. Currently we only support cookie-based
authentication, but we will add the possibility to use Auth Tokens and external OAuth
providers in the near future. Of course you can roll your own authentication mechanism
if you want to, and you can do it in an application-specific way if required.
To use the per-application authentication, you should first turn off the global
HTTP authentication (or at least restrict it to system API calls as mentioned above).
Otherwise clients will need HTTP authentication and need additional authentication by
your Foxx application.
To have global HTTP authentication turned on for system APIs but turned off for Foxx,
your server startup parameters should look like this:
--server.disable-authentication false --server.authenticate-system-only true
**Note**: During development, you may even turn off HTTP authentication completely:
--server.disable-authentication true --server.authenticate-system-only true
Please keep in mind that turning HTTP authentication off completely will allow
unauthenticated access by anyone to all API functions, so do not use this is production.
Now it's time to configure the application-specific authentication. We built a small
[demo application](https://github.com/arangodb/foxx-authentication) to demonstrate how
this works.
To use the application-specific authentication in your own app, first activate it in your controller.
!SUBSECTION Active Authentication
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`Controller.activateAuthentication(opts)`
To activate authentication for this controller, call this function before defining any routes.
In the `opts` object you can set the following keys:
* *type*: Currently we only support *cookie*, but this will change in the future
* *cookieLifetime*: An integer. Lifetime of cookies in seconds
* *cookieName*: A string used as the name of the cookie
* *sessionLifetime*: An integer. Lifetime of sessions in seconds
**Examples**
```js
app.activateAuthentication({
type: "cookie",
cookieLifetime: 360000,
cookieName: "my_cookie",
sessionLifetime: 400,
});
```
!SUBSECTION Login
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`FoxxController#login(path, opts)`
Add a route for the login. You can provide further customizations via the
the options:
* *usernameField* and *passwordField* can be used to adjust the expected attributes
in the *post* request. They default to *username* and *password*.
* *onSuccess* is a function that you can define to do something if the login was
successful. This includes sending a response to the user. This defaults to a
function that returns a JSON with *user* set to the identifier of the user and
* *key* set to the session key.
* *onError* is a function that you can define to do something if the login did not
work. This includes sending a response to the user. This defaults to a function
that sets the response to 401 and returns a JSON with *error* set to
"Username or Password was wrong".
Both *onSuccess* and *onError* should take request and result as arguments.
**Examples**
```js
app.login('/login', {
onSuccess(req, res) {
res.json({"success": true});
}
});
```
!SUBSECTION Logout
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`FoxxController#logout(path, opts)`
This works pretty similar to the logout function and adds a path to your
app for the logout functionality. You can customize it with a custom *onSuccess*
and *onError* function:
* *onSuccess* is a function that you can define to do something if the logout was
successful. This includes sending a response to the user. This defaults to a
function that returns a JSON with *message* set to "logged out".
* *onError* is a function that you can define to do something if the logout did not
work. This includes sending a response to the user. This defaults to a function
that sets the response to 401 and returns a JSON with *error* set to
"No session was found".
Both *onSuccess* and *onError* should take request and result as arguments.
**Examples**
```js
app.logout('/logout', {
onSuccess(req, res) {
res.json({"message": "Bye, Bye"});
}
});
```
!SUBSECTION Register
<!-- js/server/modules/@arangodb/foxx/controller.js -->
`FoxxController#register(path, opts)`
This works pretty similar to the logout function and adds a path to your
app for the register functionality. You can customize it with a custom *onSuccess*
and *onError* function:
* *onSuccess* is a function that you can define to do something if the registration was
successful. This includes sending a response to the user. This defaults to a
function that returns a JSON with *user* set to the created user document.
* *onError* is a function that you can define to do something if the registration did not
work. This includes sending a response to the user. This defaults to a function
that sets the response to 401 and returns a JSON with *error* set to
"Registration failed".
Both *onSuccess* and *onError* should take request and result as arguments.
You can also set the fields containing the username and password via *usernameField*
(defaults to *username*) and *passwordField* (defaults to *password*).
If you want to accept additional attributes for the user document, use the option
*acceptedAttributes* and set it to an array containing strings with the names of
the additional attributes you want to accept. All other attributes in the request
will be ignored.
If you want default attributes for the accepted attributes or set additional fields
(for example *admin*) use the option *defaultAttributes* which should be a hash
mapping attribute names to default values.
**Examples**
```js
app.register('/logout', {
acceptedAttributes: ['name'],
defaultAttributes: {
admin: false
}
});
```
!SUBSECTION Change Password
<!-- js/server/modules/@arangodb/foxx/controller.js -->
FoxxController#changePassword(route, opts)`
Add a route for the logged in user to change the password.
You can provide further customizations via the
the options:
* *passwordField* can be used to adjust the expected attribute
in the *post* request. It defaults to *password*.
* *onSuccess* is a function that you can define to do something if the change was
successful. This includes sending a response to the user. This defaults to a
function that returns a JSON with *notice* set to "Changed password!".
* *onError* is a function that you can define to do something if the login did not
work. This includes sending a response to the user. This defaults to a function
that sets the response to 401 and returns a JSON with *error* set to
"No session was found".
Both *onSuccess* and *onError* should take request and result as arguments.
**Examples**
```js
app.changePassword('/changePassword', {
onSuccess(req, res) {
res.json({"success": true});
}
});
```
!SUBSUBSECTION Restricting routes
To restrict routes, see the documentation for Documenting and Restraining the routes.