Plug.Router

Parsed documentation:
View on GitHub
A DSL to define a routing algorithm that works with Plug.

It provides a set of macros to generate routes. For example:

    defmodule AppRouter do
      use Plug.Router

      plug :match
      plug :dispatch

      get "/hello" do
        send_resp(conn, 200, "world")
      end

      match _ do
        send_resp(conn, 404, "oops")
      end
    end

Each route needs to return a connection, as per the Plug spec.
A catch-all `match` is recommended to be defined as in the example
above, otherwise routing fails with a function clause error.

The router is itself a plug, which means it can be invoked as:

    AppRouter.call(conn, AppRouter.init([]))

Notice the router contains a plug pipeline and by default it requires
two plugs: `match` and `dispatch`. `match` is responsible for
finding a matching route which is then forwarded to `dispatch`.
This means users can easily hook into the router mechanism and add
behaviour before match, before dispatch or after both.

To specify private options on `match` that can be used by plugs
before `dispatch` pass an option with key `:private` containing a map.
Example:

    get "/hello", private: %{an_option: :a_value} do
      send_resp(conn, 200, "world")
    end

These options are assigned to `:private` in the call's `Plug.Conn`.

## Routes

    get "/hello" do
      send_resp(conn, 200, "world")
    end

In the example above, a request will only match if it is a `GET` request and
the route is "/hello". The supported HTTP methods are `get`, `post`, `put`,
`patch`, `delete` and `options`.

A route can also specify parameters which will then be
available in the function body:

    get "/hello/:name" do
      send_resp(conn, 200, "hello #{name}")
    end

The `:name` parameter will also be available in the function body as
`conn.params["name"]` and `conn.path_params["name"]`.

Routes allow for globbing which will match the remaining parts
of a route and can be available as a parameter in the function
body. Also note that a glob can't be followed by other segments:

    get "/hello/*_rest" do
      send_resp(conn, 200, "matches all routes starting with /hello")
    end

    get "/hello/*glob" do
      send_resp(conn, 200, "route after /hello: #{inspect glob}")
    end

Finally, a general `match` function is also supported:

    match "/hello" do
      send_resp(conn, 200, "world")
    end

A `match` will match any route regardless of the HTTP method.
Check `match/3` for more information on how route compilation
works and a list of supported options.

## Parameter Parsing

Handling request data can be done through the
[`Plug.Parsers`](https://hexdocs.pm/plug/Plug.Parsers.html#content) plug. It
provides support for parsing URL-encoded, form-data, and JSON data as well as
providing a behaviour that others parsers can adopt.

Here is an example of `Plug.Parsers` can be used in a `Plug.Router` router to
parse the JSON-encoded body of a POST request:

    defmodule AppRouter do
      use Plug.Router

      plug :match
      plug Plug.Parsers, parsers: [:json],
                         pass:  ["application/json"],
                         json_decoder: Poison
      plug :dispatch

      post "/hello" do
        IO.inspect conn.body_params # Prints JSON POST body
        send_resp(conn, 200, "Success!")
      end
    end

It is important that `Plug.Parsers` is placed before the `:dispatch` plug in
the pipeline, otherwise the matched clause route will not receive the parsed
body in its `Plug.Conn` argument when dispatched.

`Plug.Parsers` can also be plugged between `:match` and `:dispatch` (like in
the example above): this means that `Plug.Parsers` will run only if there is a
matching route. This can be useful to perform actions such as authentication
*before* parsing the body, which should only be parsed if a route matches
afterwards.

## Error handling

In case something goes wrong in a request, the router by default
will crash, without returning any response to the client. This
behaviour can be configured in two ways, by using two different
modules:

* `Plug.ErrorHandler` - allows the developer to customize exactly
  which page is sent to the client via the `handle_errors/2` function;

* `Plug.Debugger` - automatically shows debugging and request information
  about the failure. This module is recommended to be used only in a
  development environment.

Here is an example of how both modules could be used in an application:

    defmodule AppRouter do
      use Plug.Router

      if Mix.env == :dev do
        use Plug.Debugger
      end

      use Plug.ErrorHandler

      plug :match
      plug :dispatch

      get "/hello" do
        send_resp(conn, 200, "world")
      end

      defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
        send_resp(conn, conn.status, "Something went wrong")
      end
    end

## Routes compilation

All routes are compiled to a match function that receives
three arguments: the method, the request path split on `/`
and the connection. Consider this example:

    match "/foo/bar", via: :get do
      send_resp(conn, 200, "hello world")
    end

It is compiled to:

    defp match("GET", ["foo", "bar"], conn) do
      send_resp(conn, 200, "hello world")
    end

This opens up a few possibilities. First, guards can be given
to `match`:

    match "/foo/bar/:baz" when size(baz) <= 3, via: :get do
      send_resp(conn, 200, "hello world")
    end

Second, a list of split paths (which is the compiled result) is
also allowed:

    match ["foo", "bar", baz], via: :get do
      send_resp(conn, 200, "hello world")
    end

After a match is found, the block given as `do/end` is stored
as a function in the connection. This function is then retrieved
and invoked in the `dispatch` plug.

## Options

When used, the following options are accepted by `Plug.Router`:

  * `:log_on_halt` - accepts the level to log whenever the request is halted
No suggestions.
Please help! Open an issue on GitHub if this assessment is incorrect.