How do you set etag based on content for a JSON api?

Hi all,

Is there a standard way to automatically set a weak etag in phoenix similar to how rails does?

For example, I have a JSON api that will often return the same thing and a user may request it multiple times. It can change at any moment, so I have to have max-age=0, but I’d like for an etag to be generated so that I can at least send a 304 instead of the json payload again if it hasn’t changed.

Ideally the solution would build the etag off of the map that would be sent to json and skip the json encoding in the case of a 304. Thanks!

3 Likes

This is untested, but should be sufficient:

def term2weak_etag(term), do: ~s["W/#{:erlang.phash2(term)}"]
2 Likes

You can also take a look at Plug.Static implementation for some inspiration. Etag is generated here, and set here.

4 Likes

This was all very helpful, thank you. Here’s what I ended up with for now:

defp json_with_etag(conn, term) do
  etag = ~s[W/"#{term |> :erlang.phash2 |> Integer.to_string(16)}"]

  conn =
    conn
    |> put_resp_header("cache-control", "private")
    |> put_resp_header("etag", etag)

  if etag in get_req_header(conn, "if-none-match") do
    send_resp(conn, 304, "")
  else
    json(conn, term)
  end
end
1 Like