The body of a request as it was sent, for the routes that check a signature of it: a mail provider's webhooks, GitHub's, Stripe's.
In a Phoenix app the endpoint's Plug.Parsers reads the body before the
router, and a body can be read once. A Rails engine's controller can
still read it (Rack keeps it rewindable); a library's route in Phoenix
needs the endpoint to keep a copy. One reader keeps it for every
library, given to Plug.Parsers with the app's router:
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
body_reader: {PhoenixMountable.RawBody, :read_body, [MyAppWeb.Router]},
json_decoder: Phoenix.json_library()use PhoenixMountable.Install, raw_body: true adds it to the app's
endpoint.
Which requests
Before it reads a body, the reader asks the router which plug the
request will reach (Phoenix.Router.route_info/4, through the routers
it forwards to), and keeps a copy only when that plug says it needs the
body as sent:
defmodule MyLib.Webhook do
use PhoenixMountable.RawBody
# every route to this plug
end
defmodule MyLibWeb.HookController do
use MyLibWeb, :controller
use PhoenixMountable.RawBody, only: [:create]
# the routes to these actions
endSo nothing depends on a route's path, scope or pipeline: the app can
move and rename the routes, and their bodies are kept. Every other
request is read as Plug.Conn.read_body/2 reads it, with one lookup in
the router's compiled routes the first time, and nothing kept.
The router can be a list of routers (an app with more than one endpoint's worth, or a router no forward reaches).
Reading it
In the plug, fetch/2 gives the body: the copy the reader kept, or the
body itself when the parsers left it unread (a type they pass, such as
a raw email's message/rfc822).
{:ok, body, conn} = PhoenixMountable.RawBody.fetch(conn, 1_000_000)
Summary
Functions
Marks a plug, or a controller's actions (only:), as needing the body
of their requests as it was sent.
The body of the request as it was sent, at most max bytes: the copy
read_body/3 kept, or the body read now when the parsers left it
unread. {:error, :too_large} past max, and {:error, :no_raw_body}
when the parsers read it without keeping it (the endpoint has not got
the reader, or the route's plug does not use PhoenixMountable.RawBody).
A body reader for Plug.Parsers (body_reader: {PhoenixMountable.RawBody, :read_body, [router]}): reads the body as Plug.Conn.read_body/2 does,
and keeps a copy of it for the routes that want it.
Whether the request to path_info reaches a plug that wants its body
as sent, through the routers router forwards to.
Functions
Marks a plug, or a controller's actions (only:), as needing the body
of their requests as it was sent.
@spec fetch(Plug.Conn.t(), pos_integer()) :: {:ok, binary(), Plug.Conn.t()} | {:error, :too_large | :no_raw_body | term()}
The body of the request as it was sent, at most max bytes: the copy
read_body/3 kept, or the body read now when the parsers left it
unread. {:error, :too_large} past max, and {:error, :no_raw_body}
when the parsers read it without keeping it (the endpoint has not got
the reader, or the route's plug does not use PhoenixMountable.RawBody).
@spec read_body(Plug.Conn.t(), keyword(), module() | [module()]) :: {:ok, binary(), Plug.Conn.t()} | {:more, binary(), Plug.Conn.t()} | {:error, term()}
A body reader for Plug.Parsers (body_reader: {PhoenixMountable.RawBody, :read_body, [router]}): reads the body as Plug.Conn.read_body/2 does,
and keeps a copy of it for the routes that want it.
Whether the request to path_info reaches a plug that wants its body
as sent, through the routers router forwards to.