0

我正在编写一个控制器函数,如果失败,它将在呈现 json 对象或错误对象之前检查条件(关键字是否有效)。

路由器.ex

scope "/api", DongNghiaWeb do
    pipe_through :api
    scope "/tim_kiem" do
      get "/tu/:word"   , APIWordController, :search_word
[...]

word_controller.ex

def search_word(conn, %{"word" => word}) do
  conn
  |> check_invalid_keyword(word)
  |> render("search.json", words: word |> String.trim |> Words.suggest)
end

defp check_invalid_keyword(conn, keyword) do
  unless Words.keyword_valid?(String.trim(keyword)) do
    conn
    |> put_status(400)
    |> json(%{
      error: "Invalid keyword"
    })
  end
  conn
end

word_controller_test.ex

test "response error when word is not valid", %{conn: conn} do
  response = get(conn,api_word_path(conn, :search_word, "a3d"))
    |> json_response(400)
  assert response["error"] == "Invalid keyword"
end

运行mix test时,结果会是这样的:

** (RuntimeError) expected response with status 400, got: 200, with body: {"data":[]}

但是当我尝试使用 REST 客户端(例如 Insomnia)进行测试时,json 将恢复{ error : "Invalid keyword" }正常。

4

1 回答 1

3

当错误时,您的代码正在两次写入对连接的响应Words.keyword_valid?(String.trim(keyword))

第一次写入发生在您调用时|> json(...),第二次写入发生在您调用时render。您的代码不会阻止在已经被render调用时被调用。json浏览器连接在第一次写入后结束,因此您会看到 Insomnia 的正确输出,但测试设置使用写入连接的最后一个响应。

解决这个问题需要对代码进行一些重组。这是我的做法:

def search_word(conn, %{"word" => word}) do
  if Words.keyword_valid?(String.trim(word)) do
    conn
    |> render("search.json", words: word |> String.trim() |> Words.suggest())
  else
    conn
    |> put_status(400)
    |> json(%{
      error: "Invalid keyword"
    })
  end
end

编辑:这是执行您在以下评论中要求的一种方法:

def search_word(conn, %{"word" => word}) do
  with {:ok, conn} <- check_invalid_keyword(conn, word) do
    conn
    |> render("search.json", words: word |> String.trim() |> Words.suggest())
  end
end

def check_invalid_keyword(conn, keyword) do
  if Words.keyword_valid?(String.trim(keyword)) do
    {:ok, conn}
  else
    conn
    |> put_status(400)
    |> json(%{
      error: "Invalid keyword"
    })
  end
end

当关键字无效时,返回值与with子句匹配失败,按原样返回。如果它有效,则do执行该块。

于 2018-07-20T15:22:42.587 回答