How do you run middleware functions post response in Phoenix framework?

孤者浪人 提交于 2021-01-27 22:18:41

问题


I'm developing a simple website in Elixir with Phoenix. I'd like to add some custom middleware that runs after a response has been generated. For example, in order to log the total number of bytes in each response I'd like to have a Plug like this

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

Trying to use this plug in one of the Phoenix pipelines in the router won't work though, they are all run before the response is rendered. Instead it causes a FunctionClauseError since conn.resp_body is nil. I'm not sure how to use this plug so it can run after the response is rendered.


回答1:


I think you are looking for register_before_send/2.

This allows to register callbacks that will be called before resp_body gets set to nil as explained here.

Should look like:

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

Edit: I don't think you should be using String.length for the byte size:

  • resp_body is not necessarily a string, can be an I/O-list
  • byte_size/1 should be used to count bytes, String.length/1 returns UTF-8 grapheme count

The following could do the job, but with a significant performance impact due to the need of concatenating the body:

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

:erlang.iolist_size/1 seems to work well and I suppose is much better performance-wise.



来源:https://stackoverflow.com/questions/63478471/how-do-you-run-middleware-functions-post-response-in-phoenix-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!