diff options
Diffstat (limited to 'docs/en/cowboy/2.13/guide/routing.asciidoc')
-rw-r--r-- | docs/en/cowboy/2.13/guide/routing.asciidoc | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/docs/en/cowboy/2.13/guide/routing.asciidoc b/docs/en/cowboy/2.13/guide/routing.asciidoc new file mode 100644 index 00000000..37d3e5aa --- /dev/null +++ b/docs/en/cowboy/2.13/guide/routing.asciidoc @@ -0,0 +1,271 @@ +[[routing]] +== Routing + +Cowboy does nothing by default. + +To make Cowboy useful, you need to map URIs to Erlang modules that will +handle the requests. This is called routing. + +Cowboy routes requests using the following algorithm: + +* If no configured host matches the request URI, a 400 response + is returned. + +* Otherwise, the first configured host that matches the request + URI will be used. Only the paths configured for this host will + be considered. + +* If none of the configured paths found in the previous step + match the request URI, a 404 response is returned. + +* Otherwise, the handler and its initial state are added to the + environment and the request continues to be processed. + +NOTE: It is possible to run into a situation where two hosts match a +request URI, but only the paths on the second host match the +request URI. In this case the expected result is a 404 response +because the only paths used during routing are the paths from +the first configured host that matches the request URI. + +Routes need to be compiled before they can be used by Cowboy. +The result of the compilation is the dispatch rules. + +=== Syntax + +The general structure for the routes is defined as follow. + +[source,erlang] +Routes = [Host1, Host2, ... HostN]. + +Each host contains matching rules for the host along with optional +constraints, and a list of routes for the path component. + +[source,erlang] +Host1 = {HostMatch, PathsList}. +Host2 = {HostMatch, Constraints, PathsList}. + +The list of routes for the path component is defined similar to the +list of hosts. + +[source,erlang] +PathsList = [Path1, Path2, ... PathN]. + +Finally, each path contains matching rules for the path along with +optional constraints, and gives us the handler module to be used +along with its initial state. + +[source,erlang] +Path1 = {PathMatch, Handler, InitialState}. +Path2 = {PathMatch, Constraints, Handler, InitialState}. + +Continue reading to learn more about the match syntax and the optional +constraints. + +=== Match syntax + +The match syntax is used to associate host names and paths with their +respective handlers. + +The match syntax is the same for host and path with a few subtleties. +Indeed, the segments separator is different, and the host is matched +starting from the last segment going to the first. All examples will +feature both host and path match rules and explain the differences +when encountered. + +Excluding special values that we will explain at the end of this section, +the simplest match value is a host or a path. It can be given as either +a `string()` or a `binary()`. + +[source,erlang] +---- +PathMatch1 = "/". +PathMatch2 = "/path/to/resource". + +HostMatch1 = "cowboy.example.org". +---- + +As you can see, all paths defined this way must start with a slash +character. Note that these two paths are identical as far as routing +is concerned. + +[source,erlang] +PathMatch2 = "/path/to/resource". +PathMatch3 = "/path/to/resource/". + +Hosts with and without a trailing dot are equivalent for routing. +Similarly, hosts with and without a leading dot are also equivalent. + +[source,erlang] +HostMatch1 = "cowboy.example.org". +HostMatch2 = "cowboy.example.org.". +HostMatch3 = ".cowboy.example.org". + +It is possible to extract segments of the host and path and to store +the values in the `Req` object for later use. We call these kind of +values bindings. + +The syntax for bindings is very simple. A segment that begins with +the `:` character means that what follows until the end of the segment +is the name of the binding in which the segment value will be stored. + +[source,erlang] +PathMatch = "/hats/:name/prices". +HostMatch = ":subdomain.example.org". + +If these two end up matching when routing, you will end up with two +bindings defined, `subdomain` and `name`, each containing the +segment value where they were defined. For example, the URL +`http://test.example.org/hats/wild_cowboy_legendary/prices` will +result in having the value `test` bound to the name `subdomain` +and the value `wild_cowboy_legendary` bound to the name `name`. +They can later be retrieved using `cowboy_req:binding/{2,3}`. The +binding name must be given as an atom. + +There is a special binding name you can use to mimic the underscore +variable in Erlang. Any match against the `_` binding will succeed +but the data will be discarded. This is especially useful for +matching against many domain names in one go. + +[source,erlang] +HostMatch = "ninenines.:_". + +Similarly, it is possible to have optional segments. Anything +between brackets is optional. + +[source,erlang] +PathMatch = "/hats/[page/:number]". +HostMatch = "[www.]ninenines.eu". + +You can also have imbricated optional segments. + +[source,erlang] +PathMatch = "/hats/[page/[:number]]". + +While Cowboy does not reject multiple brackets in a route, +the behavior may be undefined if the route is under-specified. +For example, this route requires constraints to determine what +is a chapter and what is a page, since they are both optional: + +[source,erlang] +PathMatch = "/book/[:chapter]/[:page]". + +You can retrieve the rest of the host or path using `[...]`. +In the case of hosts it will match anything before, in the case +of paths anything after the previously matched segments. It is +a special case of optional segments, in that it can have +zero, one or many segments. You can then find the segments using +`cowboy_req:host_info/1` and `cowboy_req:path_info/1` respectively. +They will be represented as a list of segments. + +[source,erlang] +PathMatch = "/hats/[...]". +HostMatch = "[...]ninenines.eu". + +If a binding appears twice in the routing rules, then the match +will succeed only if they share the same value. This copies the +Erlang pattern matching behavior. + +[source,erlang] +PathMatch = "/hats/:name/:name". + +This is also true when an optional segment is present. In this +case the two values must be identical only if the segment is +available. + +[source,erlang] +PathMatch = "/hats/:name/[:name]". + +If a binding is defined in both the host and path, then they must +also share the same value. + +[source,erlang] +PathMatch = "/:user/[...]". +HostMatch = ":user.github.com". + +Finally, there are two special match values that can be used. The +first is the atom `'_'` which will match any host or path. + +[source,erlang] +PathMatch = '_'. +HostMatch = '_'. + +The second is the special host match `"*"` which will match the +wildcard path, generally used alongside the `OPTIONS` method. + +[source,erlang] +HostMatch = "*". + +=== Constraints + +After the matching has completed, the resulting bindings can be tested +against a set of constraints. Constraints are only tested when the +binding is defined. They run in the order you defined them. The match +will succeed only if they all succeed. If the match fails, then Cowboy +tries the next route in the list. + +The format used for constraints is the same as match functions in +`cowboy_req`: they are provided as a list of fields which may have +one or more constraints. While the router accepts the same format, +it will skip fields with no constraints and will also ignore default +values, if any. + +Read more about xref:constraints[constraints]. + +=== Compilation + +The routes must be compiled before Cowboy can use them. The compilation +step normalizes the routes to simplify the code and speed up the +execution, but the routes are still looked up one by one in the end. +Faster compilation strategies could be to compile the routes directly +to Erlang code, but would require heavier dependencies. + +To compile routes, just call the appropriate function: + +[source,erlang] +---- +Dispatch = cowboy_router:compile([ + %% {HostMatch, list({PathMatch, Handler, InitialState})} + {'_', [{'_', my_handler, #{}}]} +]), +%% Name, TransOpts, ProtoOpts +cowboy:start_clear(my_http_listener, + [{port, 8080}], + #{env => #{dispatch => Dispatch}} +). +---- + +=== Using persistent_term + +The routes can be stored in `persistent_term` starting from +Erlang/OTP 21.2. This may give a performance improvement when +there are a large number of routes. + +To use this functionality you need to compile the routes, +store them in `persistent_term` and then inform Cowboy: + +[source,erlang] +---- +Dispatch = cowboy_router:compile([ + {'_', [{'_', my_handler, #{}}]} +]), +persistent_term:put(my_app_dispatch, Dispatch), +cowboy:start_clear(my_http_listener, + [{port, 8080}], + #{env => #{dispatch => {persistent_term, my_app_dispatch}}} +). +---- + +=== Live update + +You can use the `cowboy:set_env/3` function for updating the dispatch +list used by routing. This will apply to all new connections accepted +by the listener: + +[source,erlang] +Dispatch = cowboy_router:compile(Routes), +cowboy:set_env(my_http_listener, dispatch, Dispatch). + +Note that you need to compile the routes again before updating. + +When using `persistent_term` there is no need to call this function, +you can simply put the new routes in the storage. |