Читайте необработанное тело из разъема после парсера в Elixir
У меня есть требование проверить дайджест содержимого JSON, отправленного на сервер Phoenix. Для проверки дайджеста необходимо сырое тело. Есть ли способ получить доступ к необработанному контенту в плагине позже в конвейере, чем парсеры. Я хочу добавить следующий плагин проверки дайджеста в конец конвейера, но не могу понять, как он получает доступ к необработанному контенту, который был отправлен.
plug Plug.Parsers,
parsers: [:urlencoded, :json],
pass: ["*/*"],
json_decoder: Poison
plug Plug.MethodOverride
plug Plug.Head
plug Plug.VerifyDigest
2 ответа
Скопировано из моего ответа здесь.
Вы можете передать обычай :body_reader
возможность Plug.Parsers
для того, чтобы кэшировать тело для последующего использования.
Вы захотите не читать тело перед парсером, а вместо этого кэшировать тело для чтения позже из вашего плагина, который хочет его хэшировать.
Вариант:
:body_reader
- дополнительная замена (или обертка) дляPlug.Conn.read_body/2
предоставить функцию, которая дает доступ к необработанному телу до его анализа и удаления. Это в стандартном формате{Module, :function, [args]}
(MFA) и по умолчанию{Plug.Conn, :read_body, []}
,
Пример:
Иногда вы можете захотеть настроить, как парсер читает тело из соединения. Например, вы можете кэшировать тело для последующей проверки, например проверки подписи HTTP. Это может быть достигнуто с помощью специального считывателя тела, который будет считывать тело и сохранять его в соединении, например:
defmodule CacheBodyReader do def read_body(conn, opts) do {:ok, body, conn} = Plug.Conn.read_body(conn, opts) conn = update_in(conn.assigns[:raw_body], &[body | (&1 || [])]) {:ok, body, conn} end end
который затем может быть установлен как:
plug Plug.Parsers, parsers: [:urlencoded, :json], pass: ["text/*"], body_reader: {CacheBodyReader, :read_body, []}, json_decoder: Jason
Он был добавлен в Plug v1.5.1.
Я столкнулся с подобной проблемой, и я написал плагин в соответствии с этим (заметьте, я все еще учусь, так что это может быть сделано лучше):
defmodule Esch.Plugs.HMACValidator do
import Plug.Conn
def init(default), do: default
def call(%Plug.Conn{req_headers: req_headers} = conn, _default) do
hmac_code_tuple = List.keyfind(req_headers, "hmac_token", 0)
if hmac_code_tuple do
hmac_code = elem(hmac_code_tuple,1) |> String.downcase
{:ok, body, conn} = read_body(conn)
hmac_test_code = :crypto.hmac(:sha512, "secret", body) |> Base.encode16 |> String.downcase
if hmac_test_code == hmac_code do
params = Poison.decode!(body)
conn
|> assign(:authorized_api_call, true)
|> struct(%{:body_params => params})
else
conn |> put_resp_content_type("text/plain") |> send_resp(401, "Not Authorized") |> halt
end
else
conn
end
end
def call(conn, _default) do
conn
end
end
Приведенный выше запрос сравнивает подписанное тело HMAC с подписью HMAC в заголовке запроса.
Я обошел read_body
-проблема, анализируя JSON в том же коде, когда подпись соответствует ожидаемой подписи. conn
Действие пропускается, если запрос не соответствует типичному вызову API (в моем случае не имеет токена заголовка HMAC), поэтому body_params остается непрочитанным.
Затем я подключил вышеупомянутый плагин endpoint.ex
перед подключением Plug.Parsers.
...
plug MyApp.Plugs.HMACValidator
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
...
Я получил некоторое вдохновение от обсуждения в этом выпуске Феникса: Способ чтения тела запроса в виде строки