Can't render JSON with View

I have an endpoint defined in my routes.ex file like this:

  scope "/api", TesteWeb do
    pipe_through :api

    post "/classify", ClassifierController, :classify
  end

In my classifier_controller.ex file I have:

defmodule TesteWeb.ClassifierController do
  use TesteWeb, :controller

  def classify(conn, _params) do
    # I'm testing with hardcoded data...
    request = %{sentence: "Bom dia", model_id: "123"}
    render(conn, "show.json", request: request)
  end

end

And finally, in my classifier_view.ex file, I have:

defmodule TesteWeb.ClassifierView do
  use TesteWeb, :view

  def render("show.json", %{request: request}) do
    %{response: render_one(request,TesteWeb.ClassifierView, "response.json")}
  end

  def render("response.json", %{request: request}) do
    %{response: "ok", sentece: request.sentence}
  end

end

When I make a POST request to http://localhost:4000/api/classify, I get the following error:

[error] #PID<0.471.0> running TesteWeb.Endpoint (connection #PID<0.470.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /api/classify
** (exit) an exception was raised:
** (Phoenix.Template.UndefinedError) Could not render “response.json” for TesteWeb.ClassifierView, please define a matching clause for render/2 or define a template at “lib/teste_web/templates/classifier/*”. No templates were compiled for this module.

Why it can’t render response.json if it is defined in classifier_view.ex?

Hello and welcome,

When You want to debug something like this, You can add a catch all clause and inspect the params… Put this at the end.

  def render(any_string, message) do
    IO.puts any_string
    IO.inspect message
    %{}
  end

Then You will see why the pattern is not matched :slight_smile:

I think You have a typo here

3 Likes

Thank you very much!!

Your tip worked very well and I’ve figured out the problem.
The render_one(request,TesteWeb.ClassifierView, "response.json") function pass a structure like

%{
  classifier: %{model_id: "123", sentence: "Bom dia"},
  view_module: TesteWeb.ClassifierView,
  view_template: "response.json"
}

Where the classifier key came from?

Apart from the name You are using, it looks similar to what I do…

defmodule AppApiWeb.UserView do
  use AppApiWeb, :view
  alias AppApiWeb.UserView

  def render("index.json", %{users: users}) do
    %{data: render_many(users, UserView, "user.json")}
  end

  def render("show.json", %{user: user}) do
    %{data: render_one(user, UserView, "user.json")}
  end

  def render("user.json", %{user: user}) do
    %{
      id: user.id,
      name: user.name,
      email: user.email
    }
  end
end

Try to replace request by classifier and see how it goes. I think under the hood it will match the name of your view.

BTW from the documentation site, You can use as to specify which name to use…

https://hexdocs.pm/phoenix/Phoenix.View.html#render_one/4

So in your case, that would have been

render_one(request, TesteWeb.ClassifierView, "response.json", as: :request)
1 Like

Great tip!

1 Like