An Elixir Plug for requiring and extracting a given header.
Update your mix.exs
file and run mix deps.get
.
defp deps do
[{:plug_require_header, "~> 0.8"}]
end
Add the plug to e.g. a pipeline in a Phoenix
controller. In this case we will require the request header x-api-key
to be set,
extract its first value and assign it the connection (a Plug.Conn
) for later use
in another plug or action.
defmodule MyPhoenixApp.MyController do
use MyPhoenixApp.Web, :controller
alias Plug.Conn.Status
plug PlugRequireHeader, headers: [api_key: "x-api-key"]
plug :action
def index(conn, _params) do
conn
|> put_status(Status.code :ok)
|> text "The API key used is: #{conn.assigns[:api_key]}"
end
end
Notice how the first value required header "x-api-key"
has been extracted
and can be retrieved using conn.assigns[:api_key]
. An alternative is to use
Plug.Conn.get_req_header/2
to get all the values associated with a given header.
By default, a missing header will return a status code of 403 (forbidden) and halt
the plug pipeline, i.e. no subsequent plugs will be executed. The same is true if
the required header is explicitly set to nil
as the underlying HTTP server will
not include the header. This behaviour however is configurable.
defmodule MyPhoenixApp.MyOtherController do
use MyPhoenixApp.Web, :controller
alias Plug.Conn.Status
plug PlugRequireHeader, headers: [api_key: "x-api-key"],
on_missing: [status: 418, message: %{error: "I'm a teapot!"}, as: :json]
plug :action
def index(conn, _params) do
conn
|> put_status(Status.code :ok)
|> text "The API key used is: #{conn.assigns[:api_key]}"
end
The :on_missing
handling can be given a keyword list of options on how to handle
a missing header.
:status
- aninteger
oratom
to specify the status code. If it's an atom, it'll be looked up using thePlug.Status.code
function. Default is:forbidden
.:message
- abinary
sent as the response body. Default is an empty string.:as
- anatom
describing the content type and encoding. Currently supported alternatives are:text
for plain text and:json
for JSON. Default is:text
.
You can also provide a function that handles the missing header by specifying a
module/function pair in a tuple as the :on_missing
value.
defmodule MyPhoenixApp.MyOtherController do
use MyPhoenixApp.Web, :controller
alias Plug.Conn.Status
plug PlugRequireHeader, headers: [api_key: "x-api-key"],
on_missing: {__MODULE__, :handle_missing_header}
plug :action
def index(conn, _params) do
conn
|> put_status(Status.code :ok)
|> text("The API key used is: #{conn.assigns[:api_key]}")
end
def handle_missing_header(conn, {_connection_assignment_key, missing_header_key}) do
conn
|> send_resp(Status.code(:bad_request), "Missing header: #{missing_header_key}")
|> halt
end
end
If the header is missing or set to nil
the status code, a status code of 400
(bad request) will be returned before the plug pipeline is halted. Notice that
the function specified as a callback needs to be a public function as it'll be
invoked from another module. Also notice that the callback must return a Plug.Conn
struct.
Lastly, it's possible to extract multiple headers at the same time.
plug PlugRequireHeader, headers: [api_key: "x-api-key", magic: "x-magic"]
If extracting multiple headers and specifying an :on_missing
callback, be aware
that the callback will be invoked once for each missing header. Be careful to not send
a response as you can easily run into raising a Plug.Conn.AlreadySentError
. A way of
avoiding this is to have your callback function pattern match on the state of the conn
.
plug PlugRequireHeader, headers: [api_key: "x-api-key", secret: "x-secret"],
on_missing: {__MODULE__, :handle_missing_header}
def handle_missing_header(%Plug.Conn{state: :sent} = conn, _), do: conn
def handle_missing_header(conn, {_connection_assignment_key, missing_header_key}) do
conn
|> send_resp(Status.code(:bad_request), "Missing header: #{missing_header_key}")
|> halt
end
This example will only send a response for the first missing header.
For more information, see the full documentation.
- Fork this repository
- Create your feature branch (
git checkout -b I-say-we-take-off-and-nuke-it-from-orbit
) - Commit your changes (
git commit -am 'It is the only way to be sure!'
) - Push to the branch (
git push origin I-say-we-take-off-and-nuke-it-from-orbit
) - Create a new Pull Request