1
0
Fork 0
arangodb/Documentation/Books/Manual/Appendix/Deprecated/Actions/HtmlExample.md

299 lines
8.8 KiB
Markdown

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 request consists of a
method, normally *GET* or *POST* when using a browser, and a request path like
*/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
```html
<html>
<body>
Hello World
</body>
</html>
```
if asked *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 *_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:
@startDocuBlockInline HTML_01_routingCreateHtml
@EXAMPLE_ARANGOSH_OUTPUT{HTML_01_routingCreateHtml}
|db._routing.save({
| url: {
| match: "/hello/world"
| },
| content: {
| contentType: "text/html",
| body: "<html><body>Hello World</body></html>"
| }
});
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock HTML_01_routingCreateHtml
In order to activate the new routing, you must either restart the server or call
the internal reload function.
@startDocuBlockInline HTML_02_routingReload
@EXAMPLE_ARANGOSH_OUTPUT{HTML_02_routingReload}
require("internal").reloadRouting()
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock HTML_02_routingReload
Now use the browser and access http:// localhost:8529/hello/world
You should see the *Hello World* in our browser:
@startDocuBlockInline HTML_03_routingCurlHtml
@EXAMPLE_ARANGOSH_RUN{HTML_03_routingCurlHtml}
var url = "/hello/world";
var response = logCurlRequest('GET', url);
assert(response.code === 200);
logRawResponse(response);
db._query("FOR route IN _routing FILTER route.url.match == '/hello/world' REMOVE route in _routing")
require("internal").reloadRouting()
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock HTML_03_routingCurlHtml
Matching a URL
--------------
There are a lot of options for the *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
preferred. If there is a short and a long match, the longer match wins.
### Exact Match
If the definition is
{
url: {
match: "/hello/world"
}
}
then the match must be exact. Only the request for */hello/world* will match,
everything else, e. g. */hello/world/my* or */hello/world2*, will not match.
The following definition is a short-cut for an exact match.
{
url: "/hello/world"
}
**Note**: While the two definitions will result in the same URL
matching, there is a subtle difference between them:
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
(not implemented) error.
### Prefix Match
If the definition is
{
url: {
match: "/hello/world/*"
}
}
then the match can be a prefix match. The requests for */hello/world*,
*/hello/world/my*, and */hello/world/how/are/you* will all match. However
*/hello/world2* does not match. Prefix matches within a URL part,
i. e. */hello/world**, are not allowed. The wildcard must occur at the end,
i. e.
/hello/*/world
is also disallowed.
If you define two routes
{ url: { match: "/hello/world/*" } }
{ url: { match: "/hello/world/emil" } }
then the second route will be used for */hello/world/emil* because it is more
specific.
### 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
{
url: {
match: "/hello/:name/world"
}
}
then the URL must have three parts, the first part being *hello* and the third
part *world*. For example, */hello/emil/world* will match, while
*/hello/emil/meyer/world* will not.
### Constraint Match
A constraint match is similar to a parameterized match, but the parameters can
carry constraints.
If the definition is
{
url: {
match: "/hello/:name/world",
constraint: {
name: "/[a-z]+/"
}
}
}
then the URL must have three parts, the first part being *hello* and the third
part *world*. The second part must be all lowercase.
It is possible to use more then one constraint for the same URL part.
{
url: {
match: "/hello/:name|:id/world",
constraint: {
name: "/[a-z]+/", id: "/[0-9]+/"
}
}
}
### Optional Match
An optional match is similar to a parameterized match, but the last parameter is
optional.
If the definition is
{
url: {
match: "/hello/:name?",
constraint: {
name: "/[a-z]+/"
}
}
}
then the URL */hello* and */hello/emil* will match.
If the definitions are
{ url: { match: "/hello/world" } }
{ url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } }
{ url: { match: "/hello/*" } }
then the URL */hello/world* will be matched by the first route, because it is
the most specific. The URL */hello/you* will be matched by the second route,
because it is more specific than the prefix match.
### Method Restriction
You can restrict the match to specific HTTP methods.
If the definition is
{
url: {
match: "/hello/world",
methods: [ "post", "put" ]
}
}
then only HTTP *POST* and *PUT* requests will match.
Calling with a different HTTP method will result in an HTTP 501 error.
Please note that if *url* is defined as a simple string, then only the
HTTP methods *GET* and *HEAD* will be allowed, an all other methods will be
disabled:
{
url: "/hello/world"
}
### 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
@startDocuBlockInline HTML_04_routingCreateMultiPath
@EXAMPLE_ARANGOSH_OUTPUT{HTML_04_routingCreateMultiPath}
|db._routing.save({
| url: { match: "/hello/world" },
content: { contentType: "text/plain", body: "Match No 1"} });
|db._routing.save({
| url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } },
content: { contentType: "text/plain", body: "Match No 2"} });
|db._routing.save({
| url: { match: "/:something/world" },
content: { contentType: "text/plain", body: "Match No 3"} });
|db._routing.save({
| url: { match: "/hi/*" },
content: { contentType: "text/plain", body: "Match No 4"} });
require("internal").reloadRouting()
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock HTML_04_routingCreateMultiPath
Then
@startDocuBlockInline HTML_05_routingGetMultiPath
@EXAMPLE_ARANGOSH_RUN{HTML_05_routingGetMultiPath}
| var url = ["/hello/world",
| "/hello/emil",
| "/your/world",
"/hi/you"];
| for (let i = 0; i < 4; i++) {
| var response = logCurlRequest('GET', url[i]);
| assert(response.code === 200);
| assert(parseInt(response.body.substr(-1)) === (i + 1))
| logRawResponse(response);
}
db._query("FOR route IN _routing FILTER route.content.contentType == 'text/plain' REMOVE route in _routing")
require("internal").reloadRouting()
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock HTML_05_routingGetMultiPath
You can write the following document into the *_routing* collection
to test the above examples.
{
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" },
]
}