尝试从数据库中获取错误并 return 导致 http 响应

error trying to fetch from database and return result in http response

如果我在 iex 中 运行 Api.Category |> Api.Repo.all 我在终端中得到这个响应(基本上我从 "categories" 数据库 table 中得到两行) :

iex(1)> Api.Category |> Api.Repo.all

16:21:55.775 [debug] QUERY OK source="categories" db=5.2ms decode=6.3ms
SELECT c0."id", c0."name" FROM "categories" AS c0 []
[%Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 1,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 2,
  name: "Meals"}]

我试图在 HTTP 响应中发回该响应,但收到此错误(我认为它只是没有从数据库中检索任何内容)。

Poison.EncodeError at GET /categories unable to encode value: {nil, "categories"} lib/poison/encoder.ex
378  def encode(%{__struct__: _} = struct, options) do
379    Poison.Encoder.Map.encode(Map.from_struct(struct), options)
380  end
381
382  def encode(value, _options) do
383    raise Poison.EncodeError, value: value
384  end
385end

这是我的函数,它试图获取 "categories" 数据库 table 中的条目,并在 http 响应中 return 它们:

  def getCategories(conn) do
    categories = Api.Category |> Api.Repo.all
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{categories: categories}))
  end

我做错了什么?

我设置了连接:

application.ex:

defmodule Api.Application do
  use Application
  def start( _type, _args ) do
    import Supervisor.Spec, warn: false
    children = [
      worker(__MODULE__, [], function: :run),
      supervisor(Api.Repo, []),
    ]
    opts = [strategy: :one_for_one, name: Api.Supervisor]
    Supervisor.start_link(children, opts)
  end
  def run do
    { :ok, _ } = Plug.Adapters.Cowboy.http Api.Router, []
  end
end

repo.ex 有我所有的数据库查询(包括 getCategories,它应该在控制器中)有人告诉我这很奇怪但是嘿,迭代 1,我只想让它先工作:

defmodule Api.Repo do
  use Ecto.Repo, otp_app: :api
  require Ecto.Query
  import Plug.Conn

  def insertCategories do 
    categories = [
      %Api.Category{name: "Grocery Products"},
      %Api.Category{name: "Meals"}
    ]
    Enum.each(categories, fn (category) -> insert(category) end)
  end

  def insertSubcategories do 
    subcategories = [
      %Api.Subcategory{name: "Meat"},
      %Api.Subcategory{name: "Dairy"},
      %Api.Subcategory{name: "Confectionary"},
      %Api.Subcategory{name: "Dessert"},
      %Api.Subcategory{name: "Baking"},
      %Api.Subcategory{name: "Condiments"},
      %Api.Subcategory{name: "Beverages"},
      %Api.Subcategory{name: "African"},
      %Api.Subcategory{name: "Argentine"},
      %Api.Subcategory{name: "Asian"},
      %Api.Subcategory{name: "Asian Fusion"},
      %Api.Subcategory{name: "BBQ"},
      %Api.Subcategory{name: "Bakery"},
      %Api.Subcategory{name: "Beverages"},
      %Api.Subcategory{name: "Brazilian"},
      %Api.Subcategory{name: "Breakfast"},
      %Api.Subcategory{name: "British"},
      %Api.Subcategory{name: "Cafe"},
      %Api.Subcategory{name: "Cambodian"},
      %Api.Subcategory{name: "Chinese"},
      %Api.Subcategory{name: "Coffee and Tea"},
      %Api.Subcategory{name: "Contemporary"},
      %Api.Subcategory{name: "Continental"},
      %Api.Subcategory{name: "Deli"},
      %Api.Subcategory{name: "Desserts"},
      %Api.Subcategory{name: "Drinks Only"},
      %Api.Subcategory{name: "European"},
      %Api.Subcategory{name: "Fijian"},
      %Api.Subcategory{name: "Filipino"},
      %Api.Subcategory{name: "Finger Food"},
      %Api.Subcategory{name: "Fish and Chips"},
      %Api.Subcategory{name: "French Fusion"},
      %Api.Subcategory{name: "German"},
      %Api.Subcategory{name: "Greek"},
      %Api.Subcategory{name: "Grill"},
      %Api.Subcategory{name: "Healthy Food"},
      %Api.Subcategory{name: "Ice Cream"},
      %Api.Subcategory{name: "Indian"},
      %Api.Subcategory{name: "Indonesian"},
      %Api.Subcategory{name: "International"},
      %Api.Subcategory{name: "Irish"},
      %Api.Subcategory{name: "Italian"},
      %Api.Subcategory{name: "Japanese"},
      %Api.Subcategory{name: "Jewish"},
      %Api.Subcategory{name: "Juices"},
      %Api.Subcategory{name: "Kiwi"},
      %Api.Subcategory{name: "Korean"},
      %Api.Subcategory{name: "Latin"},
      %Api.Subcategory{name: "American"},
      %Api.Subcategory{name: "Lebanese"},
      %Api.Subcategory{name: "Malaysian"},
      %Api.Subcategory{name: "Mediterranean"},
      %Api.Subcategory{name: "Mexican"},
      %Api.Subcategory{name: "Middle Eastern"},
      %Api.Subcategory{name: "Mongolian"},
      %Api.Subcategory{name: "Moroccan"},
      %Api.Subcategory{name: "Nepalese"},
      %Api.Subcategory{name: "North Indian"},
      %Api.Subcategory{name: "Pacific"},
      %Api.Subcategory{name: "Persian"},
      %Api.Subcategory{name: "Pizza"},
      %Api.Subcategory{name: "Portuguese"},
      %Api.Subcategory{name: "Pub Food"},
      %Api.Subcategory{name: "Seafood"},
      %Api.Subcategory{name: "Singaporean"},
      %Api.Subcategory{name: "South Indian"},
      %Api.Subcategory{name: "Spanish"},
      %Api.Subcategory{name: "Sri Lankan"},
      %Api.Subcategory{name: "Steakhouse"},
      %Api.Subcategory{name: "Street Food"},
      %Api.Subcategory{name: "Sushi"},
      %Api.Subcategory{name: "Taiwanese"},
      %Api.Subcategory{name: "Thai"},
      %Api.Subcategory{name: "Turkish"},
      %Api.Subcategory{name: "Vietnamese"},
    ]
    Enum.each(subcategories, fn (subcategory) -> insert(subcategory) end)
  end

  def insertCategorySubcategories do 
    categorySubcategories = [
      %Api.CategorySubcategory{c_id: 1, s_id: 1},
      %Api.CategorySubcategory{c_id: 1, s_id: 2},
      %Api.CategorySubcategory{c_id: 1, s_id: 3},
      %Api.CategorySubcategory{c_id: 1, s_id: 4},
      %Api.CategorySubcategory{c_id: 1, s_id: 5},
      %Api.CategorySubcategory{c_id: 1, s_id: 6},
      %Api.CategorySubcategory{c_id: 1, s_id: 7},
      %Api.CategorySubcategory{c_id: 2, s_id: 8},
      %Api.CategorySubcategory{c_id: 2, s_id: 9},
      %Api.CategorySubcategory{c_id: 2, s_id: 10},
      %Api.CategorySubcategory{c_id: 2, s_id: 11},
      %Api.CategorySubcategory{c_id: 2, s_id: 12},
      %Api.CategorySubcategory{c_id: 2, s_id: 13},
      %Api.CategorySubcategory{c_id: 2, s_id: 14},
      %Api.CategorySubcategory{c_id: 2, s_id: 15},
      %Api.CategorySubcategory{c_id: 2, s_id: 16},
      %Api.CategorySubcategory{c_id: 2, s_id: 17},
      %Api.CategorySubcategory{c_id: 2, s_id: 18},
      %Api.CategorySubcategory{c_id: 2, s_id: 19},
      %Api.CategorySubcategory{c_id: 2, s_id: 20},
      %Api.CategorySubcategory{c_id: 2, s_id: 21},
      %Api.CategorySubcategory{c_id: 2, s_id: 23},
      %Api.CategorySubcategory{c_id: 2, s_id: 24},
      %Api.CategorySubcategory{c_id: 2, s_id: 25},
      %Api.CategorySubcategory{c_id: 2, s_id: 26},
      %Api.CategorySubcategory{c_id: 2, s_id: 27},
      %Api.CategorySubcategory{c_id: 2, s_id: 28},
      %Api.CategorySubcategory{c_id: 2, s_id: 29},
      %Api.CategorySubcategory{c_id: 2, s_id: 30},
      %Api.CategorySubcategory{c_id: 2, s_id: 31},
      %Api.CategorySubcategory{c_id: 2, s_id: 32},
      %Api.CategorySubcategory{c_id: 2, s_id: 33},
      %Api.CategorySubcategory{c_id: 2, s_id: 34},
      %Api.CategorySubcategory{c_id: 2, s_id: 35},
      %Api.CategorySubcategory{c_id: 2, s_id: 36},
      %Api.CategorySubcategory{c_id: 2, s_id: 37},
      %Api.CategorySubcategory{c_id: 2, s_id: 38},
      %Api.CategorySubcategory{c_id: 2, s_id: 39},
      %Api.CategorySubcategory{c_id: 2, s_id: 40},
      %Api.CategorySubcategory{c_id: 2, s_id: 41},
      %Api.CategorySubcategory{c_id: 2, s_id: 42},
      %Api.CategorySubcategory{c_id: 2, s_id: 43},
      %Api.CategorySubcategory{c_id: 2, s_id: 44},
      %Api.CategorySubcategory{c_id: 2, s_id: 45},
      %Api.CategorySubcategory{c_id: 2, s_id: 46},
      %Api.CategorySubcategory{c_id: 2, s_id: 47},
      %Api.CategorySubcategory{c_id: 2, s_id: 49},
      %Api.CategorySubcategory{c_id: 2, s_id: 50},
      %Api.CategorySubcategory{c_id: 2, s_id: 51},
      %Api.CategorySubcategory{c_id: 2, s_id: 52},
      %Api.CategorySubcategory{c_id: 2, s_id: 53},
      %Api.CategorySubcategory{c_id: 2, s_id: 54},
      %Api.CategorySubcategory{c_id: 2, s_id: 55},
      %Api.CategorySubcategory{c_id: 2, s_id: 56},
      %Api.CategorySubcategory{c_id: 2, s_id: 57},
      %Api.CategorySubcategory{c_id: 2, s_id: 58},
      %Api.CategorySubcategory{c_id: 2, s_id: 59},
      %Api.CategorySubcategory{c_id: 2, s_id: 60},
      %Api.CategorySubcategory{c_id: 2, s_id: 61},
      %Api.CategorySubcategory{c_id: 2, s_id: 62},
      %Api.CategorySubcategory{c_id: 2, s_id: 63},
      %Api.CategorySubcategory{c_id: 2, s_id: 64},
      %Api.CategorySubcategory{c_id: 2, s_id: 65},
      %Api.CategorySubcategory{c_id: 2, s_id: 66},
      %Api.CategorySubcategory{c_id: 2, s_id: 67},
      %Api.CategorySubcategory{c_id: 2, s_id: 68},
      %Api.CategorySubcategory{c_id: 2, s_id: 69},
      %Api.CategorySubcategory{c_id: 2, s_id: 70},
      %Api.CategorySubcategory{c_id: 2, s_id: 71},
      %Api.CategorySubcategory{c_id: 2, s_id: 72},
      %Api.CategorySubcategory{c_id: 2, s_id: 73},
      %Api.CategorySubcategory{c_id: 2, s_id: 74},
    ]
    Enum.each(categorySubcategories, fn (categorySubcategory) -> insert(categorySubcategory) end)
  end

  def getCategories(conn) do
    insertCategories
    categories = all Api.Category
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{categories: categories}))
  end

  def getSubcategories do
    Api.Subcategory |> all
  end
end

router.ex

的相关部分
  get "/categories/" do
    [controller] = ["repo"]
    Api.Repo.getCategories(conn)
  end

我浏览到 localhost:4000/categories 并在顶部看到错误。

IO.puts(类别)的输出 - 它有很多重复的行,但这是另一个问题,哈哈。

16:59:54.922 [debug] QUERY OK source="categories" db=5.2ms decode=3.7ms
SELECT c0."id", c0."name" FROM "categories" AS c0 []
[%Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 1,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 2,
  name: "Meals"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 3,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 4,
  name: "Meals"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 5,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 6,
  name: "Meals"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 7,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 8,
  name: "Meals"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 9,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 10,
  name: "Meals"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 11,
  name: "Grocery Products"},
 %Api.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 12,
  name: "Meals"}]

16:59:55.038 [error] #PID<0.340.0> running Api.Router terminated
Server: localhost:4000 (http)
Request: GET /categories
** (exit) an exception was raised:
    ** (Poison.EncodeError) unable to encode value: {nil, "categories"}
        (poison) lib/poison/encoder.ex:383: Poison.Encoder.Any.encode/2
        (poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
        (poison) lib/poison/encoder.ex:228: Poison.Encoder.Map."-encode/3-lists^foldl/2-0-"/3
        (poison) lib/poison/encoder.ex:228: Poison.Encoder.Map.encode/3
        (poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
        (poison) lib/poison/encoder.ex:228: Poison.Encoder.Map."-encode/3-lists^foldl/2-0-"/3
        (poison) lib/poison/encoder.ex:228: Poison.Encoder.Map.encode/3
        (poison) lib/poison/encoder.ex:259: anonymous fn/3 in Poison.Encoder.List.encode/3

您需要为您的结构自定义实现 Poison.Encoder,这样它就不会尝试对结构的 __meta__ 字段进行编码(其中包含一个元组 Poison 不处理)。最简单的方法是将 @derive 添加到包含要编码的字段名称的结构中:

defmodule Api.Category do
  ...
  @derive {Poison.Encoder, only: [:id, :name]} # <- add this
  schema "categories" do
    ...
  end
  ...
end