1
0
Fork 0
arangodb/Documentation/Books/Users/Foxx/FoxxController.mdpp

673 lines
22 KiB
Plaintext

!CHAPTER Details on FoxxController
`new FoxxController(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*
app = new Controller(applicationContext, {
urlPrefix: "/meadow"
});
<!--
@copydetails JSF_foxx_controller_initializer
-->
!SUBSECTION HTTP Methods
!SUBSUBSECTION Get
`FoxxController::get(path, callback)`
This handles requests from the HTTP verb get.
When defining a route you can also define a so called 'parameterized' path like /goose/:barn. In this case you can later get the value the user provided for barn via the params function (see the Request object).
*Examples*
app.get('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
!SUBSUBSECTION Head
`FoxxController::head(path, callback)`
This handles requests from the HTTP verb head. You have to give a function as callback. It will get a request and response object as its arguments
!SUBSUBSECTION Post
`FoxxController::post(path, callback)`
This handles requests from the HTTP verb post. See above for the arguments you can give.
*Examples*
app.post('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
!SUBSUBSECTION Put
`FoxxController::put(path, callback)`
This handles requests from the HTTP verb put. See above for the arguments you can give.
*Examples*
app.put('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
!SUBSUBSECTION Patch
`FoxxController::patch(path, callback)`
This handles requests from the HTTP verb patch. See above for the arguments you can give.
*Examples*
app.patch('/goose/barn', function (req, res) {
// Take this request and deal with it!
});
!SUBSUBSECTION Delete
`FoxxController::delete(path, callback)`
This handles requests from the HTTP verb delete. See above for the arguments you can give.
**Note**
If you are uncomfortable with using JavaScript keywords as property names, you can use the alias *del* instead.
<!--
### Get
@copydetails JSF_foxx_controller_get
### Head
@copydetails JSF_foxx_controller_head
### Post
@copydetails JSF_foxx_controller_post
### Put
@copydetails JSF_foxx_controller_put
### Patch
@copydetails JSF_foxx_controller_patch
### Delete
@copydetails JSF_foxx_controller_delete
-->
!SUBSECTION Documenting and constraining a specific route
If you now want to document your route, you can use JSDoc style comments (a
multiline comment block with the first line starting with */*** instead
of */**) above your routes to do that:
/** Get all foxxes
*
* If you want to get all foxxes, please use this
* method to do that.
*/
app.get("/foxxes", function () {
// ...
});
<!--
@verbinclude foxx-doc-comment
-->
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:
!SUBSUBSECTION Path Param
If you defined a route "/foxx/:id", you can constrain which format a path parameter (/foxx/12) can have by giving it a type. We currently support the following types:
* int
* string
You can also provide a description of this parameter.
*Examples*
app.get("/foxx/:id", function {
// Do something
}).pathParam("id", {
description: "Id of the Foxx",
type: "int"
});
!SUBSUBSECTION Query Param
FoxxController::queryParam(id, options)
Describe a query parameter:
If you defined a route "/foxx", you can constrain which format a query parameter (/foxx?a=12) can have by giving it a type. We currently support the following types:
* int
* string
You can also provide a description of this parameter, if it is required and if you can provide the parameter multiple times.
*Examples*
app.get("/foxx", function {
// Do something
}).queryParam("id", {
description: "Id of the Foxx",
type: "int",
required: true,
allowMultiple: false
});
!SUBSUBSECTION Body Param
`FoxxController::bodyParam(paramName, description, Model)`
Expect the body of the request to be a JSON with the attributes you annotated in your model. It will appear alongside the provided description in your Documentation. This will initialize a Model with the data and provide it to you via the params as paramName. For information about how to annotate your models, see the Model section.
*Error Response*
`FoxxController::errorResponse(errorClass, code, description)`
Define a reaction to a thrown error for this route: If your handler throws an error of the defined errorClass, it will be caught and the response will have 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 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*
/* define our own error type, FoxxyError */
var FoxxyError = function (message) {
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(FoxxyError, 303, "This went completely wrong. Sorry!", function (e) {
return {
code: 123,
desc: e.message
};
});
!SUBSUBSECTION onlyIf
`FoxxController::onlyIf(check)`
Provide it with a function that throws an exception if the normal processing should not be executed. Provide an errorResponse to define the behavior in this case. This can be used for authentication or authorization for example.
*Examples*
app.get("/foxx", function {
// Do something
}).onlyIf(aFunction).errorResponse(ErrorClass, 303, "This went completely wrong. Sorry!");
!SUBSUBSECTION onlyIfAuthenticated
`FoxxController::onlyIf(code, reason)`
Please activate authentification for this app if you want to use this function. 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*
app.get("/foxx", function {
// Do something
}).onlyIfAuthenticated(401, "You need to be authenticated");
<!--
### Path Param
@copydetails JSF_foxx_RequestContext_pathParam
### Query Param
@copydetails JSF_foxx_RequestContext_queryParam
### Body Param
@copydetails JSF_foxx_RequestContext_bodyParam
### Error Response
@copydetails JSF_foxx_RequestContext_errorResponse
### onlyIf
@copydetails JSF_foxx_RequestContext_onlyIf
### onlyIfAuthenticated
@copydetails JSF_foxx_RequestContext_onlyIfAuthenticated
-->
!SUBSECTION Documenting and constraining all routes of a controller
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:
!SUBSECTION Error Response
`RequestContextBuffer::errorResponse(errorClass, code, description)`
Defines an errorResponse for all routes of this controller. For details on errorResponse see the according method on routes.
*Examples*
app.allroutes.errorResponse(FoxxyError, 303, "This went completely wrong. Sorry!");
app.get("/foxx", function {
// Do something
});
onlyIf
`RequestContextBuffer::onlyIf(code, reason)`
Defines an onlyIf for all routes of this controller. For details on onlyIf see the according method on routes.
*Examples*
app.allroutes.onlyIf(myPersonalCheck);
app.get("/foxx", function {
// Do something
});
onlyIfAuthenticated
RequestContextBuffer::errorResponse(errorClass, code, description)
Defines an onlyIfAuthenticated for all routes of this controller. For details on onlyIfAuthenticated see the according method on routes.
*Examples*
app.allroutes.onlyIfAuthenticated(401, "You need to be authenticated");
app.get("/foxx", function {
// Do something
});
<!--
### Error Response
@copydetails JSF_foxx_RequestContextBuffer_errorResponse
### onlyIf
@copydetails JSF_foxx_RequestContextBuffer_onlyIf
### onlyIfAuthenticated
@copydetails JSF_foxx_RequestContextBuffer_onlyIfAuthenticated
-->
!SUBSECTION 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).
!SUBSUBSECTION Before
`FoxxController::before(path, callback)`
The before function takes a path on which it should watch and a function that it should execute before the routing takes place. If you do omit the path, the function will be executed before each request, no matter the path. Your function gets a Request and a Response object.
*Examples*
app.before('/high/way', function(req, res) {
//Do some crazy request logging
});
!SUBSUBSECTION After
`FoxxController::after(path, callback)`
This works pretty similar to the before function. But it acts after the execution of the handlers (Big surprise, I suppose).
*Examples*
app.after('/high/way', function(req, res) {
//Do some crazy response logging
});
<!--
### Before
@copydetails JSF_foxx_controller_before
### After
@copydetails JSF_foxx_controller_after
-->
!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 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
* *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:
!SUBSUBSECTION Body
`request.body()`
Get the JSON parsed body of the request. If you need the raw version, please refer to the rawBody function.
!SUBSUBSECTION Raw Body
`request.rawBody()`
The raw request body, not parsed. Just a String.
!SUBSUBSECTION Params
`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.
<!--
### Body
@copydetails JSF_foxx_BaseMiddleware_request_body
### Raw Body
@copydetails JSF_foxx_BaseMiddleware_request_rawBody
### Params
@copydetails JSF_foxx_BaseMiddleware_request_params
-->
!SUBSECTION 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.
!SUBSUBSECTION Status
`response.status(code)`
Set the status code of your response.
*Examples*
response.status(404);
!SUBSUBSECTION Set
`response.set(key, value)`
Set a header attribute.
*Examples*
response.set("Content-Length", 123);
response.set("Content-Type", "text/plain");
// or alternatively:
response.set({
"Content-Length": "123",
"Content-Type": "text/plain"
});
!SUBSUBSECTION JSON
`response.json(object)`
Set the content type to JSON and the body to the JSON encoded object you provided.
*Examples*
response.json({'born': 'December 12, 1915'});
<!--
### Status
@copydetails JSF_foxx_BaseMiddleware_response_status
### Set
@copydetails JSF_foxx_BaseMiddleware_response_set
### JSON
@copydetails JSF_foxx_BaseMiddleware_response_json
-->
!SUBSECTION 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 @ref CommandLineArangoDisableAuthentication
"server.disable-authentication".
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 @ref CommandLineArangoAuthenticateSystemOnly
"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:
*FoxxController::activateAuthentication(opts)*
To activate authentication for this authentication, first call this function. Provide the following arguments:
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*
app.activateAuthentication({
type: "cookie",
cookieLifetime: 360000,
cookieName: "my_cookie",
sessionLifetime: 400,
});
!SUBSUBSECTION Adding a login route
*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*
app.login('/login', {
onSuccess: function (req, res) {
res.json({"success": true});
}
});
!SUBSUBSECTION Adding a logout route
*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*
app.logout('/logout', {
onSuccess: function (req, res) {
res.json({"message": "Bye, Bye"});
}
});
!SUBSUBSECTION Adding a register route
*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*
app.register('/logout', {
acceptedAttributes: ['name'],
defaultAttributes: {
admin: false
}
});
!SUBSUBSECTION Adding a change password route
*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*
app.changePassword('/changePassword', {
onSuccess: function (req, res) {
res.json({"success": true});
}
});
<!--
@copydetails JSF_foxx_controller_activateAuthentication
### Adding a login route
@copydetails JSF_foxx_controller_login
### Adding a logout route
@copydetails JSF_foxx_controller_logout
### Adding a register route
@copydetails JSF_foxx_controller_register
### Adding a change password route
@copydetails JSF_foxx_controller_changePassword
-->
!SUBSUBSECTION Restricting routes
To restrict routes, see the documentation for Documenting and Restraining the routes.