Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phoenix: render JSON from a template

I'm trying to create a very simple case: a controller action which renders a static JSON, from a template.

controller:

defmodule MyApp.TestController do
  use Phoenix.Controller

  def show(conn, _params) do
    render(conn, "show.json")
  end
end

view:

defmodule MyApp.TestView do
  use MyApp.Web, :view
end

show.json.eex:

{
  "message": "Hello, world!"
}

The problem is, I get the proper JSON response, but JSON-encoded:

"{\n  \"message\": \"Hello, world!\"\n}"

Any idea why, and how to solve it?

/Edit:

I found out that I can work around the problem by renaming the template to something other than json (plus explicitly setting response type, of course), so obviously JSON templates are additionally encoded. But why, who would want such a thing?

like image 940
Mladen Jablanović Avatar asked Sep 29 '16 11:09

Mladen Jablanović


1 Answers

After further investigation and talking with people on Phoenix Slack channel, I have a clearer picture about what's going on:

Phoenix is agnostic when it comes to deciding whether the content comes from a template or from a data structure in the view. render/2 from the controller happily takes anything and converts it to JSON.

I wrote up a more detailed blog post about this issue, along with several approaches in addressing it, and in my opinion every is a workaround for the inherent problem in Phoenix (which, admittedly, is far from critical).

Essentially, one should avoid executing Poison.encode function, which gets called from render_to_iostream function. You can do that either by not using .json as the template extension, by directly calling Phoenix.View.render from the controller, or by creating custom encoder and template engine to pass some metadata along with the data to be output.

like image 122
Mladen Jablanović Avatar answered Oct 18 '22 01:10

Mladen Jablanović