2

我正在与 Phoenix 一起在 Elixir 中开发一个简单的网站。我想添加一些在生成响应后运行的自定义中间件。例如,为了记录每个响应中的总字节数,我想要一个这样的 Plug

defmodule HelloWeb.Plugs.ByteLogger do
  import Plug.Conn
  require Logger

  def init(default), do: default

  def call(conn, default) do
    log("bytes sent: #{String.length(conn.resp_body)}")
  end
end

尝试在路由器中的一个 Phoenix 管道中使用这个插件是行不通的,它们都在响应呈现之前运行。相反,它会导致一个FunctionClauseErrorsince conn.resp_bodyis nil。我不确定如何使用此插件,以便在呈现响应后运行它。

4

1 回答 1

2

我认为您正在寻找register_before_send/2

这允许注册将在resp_body设置为 nil 之前调用的回调,如此处所述

应该看起来像:

defmodule HelloWeb.Plugs.ByteLogger do
  import Plug.Conn
  require Logger

  def init(default), do: default

  def call(conn, default) do
    register_before_send(conn, fn conn ->
      log("bytes sent: #{String.length(conn.resp_body)}")

      conn
    end)
  end
end

编辑:我不认为你应该使用String.length字节大小:

  • resp_body不一定是字符串,可以是 I/O 列表
  • byte_size/1应该用于计算字节数,String.length/1返回 UTF-8 字形计数

以下可以完成这项工作,但由于需要连接主体,因此会对性能产生重大影响:

conn.resp_body |> to_string() |> byte_size()

:erlang.iolist_size/1似乎运作良好,我想在性能方面要好得多。

于 2020-08-19T02:56:40.437 回答