AJAX Post 请求错误:“不能为空”,[验证::required] 在 POST 正文中发送 JSON 对象时

Posted

技术标签:

【中文标题】AJAX Post 请求错误:“不能为空”,[验证::required] 在 POST 正文中发送 JSON 对象时【英文标题】:AJAX Post request error: "can't be blank", [validation: :required] When sending JSON object in POST body 【发布时间】:2017-10-22 19:05:08 【问题描述】:

当请求在OPTIONS "/products" 到达我的路由器时,我收到此错误

错误

14:45:33.433 [error] #PID<0.339.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: OPTIONS /products
** (exit) an exception was raised:
    ** (Poison.EncodeError) unable to encode value: :brand, "can't be blank", [validation: :required]
        (poison) lib/poison/encoder.ex:383: Poison.Encoder.Any.encode/2
        (poison) lib/poison/encoder.ex:259: anonymous fn/3 in Poison.Encoder.List.encode/3
        (poison) lib/poison/encoder.ex:260: Poison.Encoder.List."-encode/3-lists^foldr/2-1-"/3
        (poison) lib/poison/encoder.ex:260: Poison.Encoder.List.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:227: anonymous fn/4 in Poison.Encoder.Map.encode/3

显示在网络请求期间在前端捕获的错误的图像:

从上图来看,产品 JSON 对象似乎确实被发送了,因此由于某种原因它没有正确地将 http 帖子正文映射到 elixir 变更集。

连接日志:

Interactive Elixir (1.4.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> %
%Plug.Connadapter: Plug.Adapters.Cowboy.Conn, :..., assigns: %,
 before_send: [], body_params: %,
 cookies: %Plug.Conn.Unfetchedaspect: :cookies, halted: false,
 host: "192.168.20.3", method: "OPTIONS", owner: #PID<0.339.0>, params: %,
 path_info: ["products"], path_params: %, peer: 192, 168, 20, 3, 63793,
 port: 4000,
 private: %plug_route: #Function<1.14347947/1 in Api.Router.do_match/4>,
 query_params: %, query_string: "", remote_ip: 192, 168, 20, 3,
 req_cookies: %Plug.Conn.Unfetchedaspect: :cookies,
 req_headers: ["host", "192.168.20.3:4000", "connection", "keep-alive",
  "access-control-request-method", "POST", "origin", "http://evil.com/",
  "user-agent",
   "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/58.0.3029.110 Safari/
537.36",
  "access-control-request-headers", "content-type,x-requested-with",
  "accept", "*/*", "referer", "http://localhost:8081/debugger-ui",
  "accept-encoding", "gzip, deflate, sdch",
  "accept-language", "en-GB,en-US;q=0.8,en;q=0.6"], request_path: "/products",
 resp_body: nil, resp_cookies: %,
 resp_headers: ["cache-control", "max-age=0, private, must-revalidate"],
 scheme: :http, script_name: [], secret_key_base: nil, state: :unset,
 status: nil
%Api.Product__meta__: #Ecto.Schema.Metadata<:built, "products">, brand: nil,
 description: nil, id: nil, image: nil, name: nil, numberOfVotes: nil,
 rating: nil

router.ex

defmodule Api.Router do
  use Plug.Router

  if Mix.env == :dev do
    use Plug.Debugger
  end
  plug :match
  plug Plug.Parsers, parsers: [:json],
                   pass:  ["application/json"],
                   json_decoder: Poison
  plug :dispatch

  get "/favicon.ico" do
    # Api.Repo.getCategories(conn)
  end

  get "/categories/" do
    Api.Repo.getCategories(conn)
  end

  options "/categories/" do
    Api.Repo.getCategories(conn)
  end

  post "/products" do
    Api.Repo.insertProduct(conn, conn.body_params)
  end

  options "/products" do
    IO.puts inspect conn.body_params
    Api.Repo.insertProduct(conn, conn.body_params)
  end

  get "/products" do
    Api.Repo.insertProduct(conn, conn.body_params)
  end
end

在 repo.ex 中

  def insertProduct(conn, product) do
    IO.inspect(conn)
    changeset = Api.Product.changeset(%Api.Product, product)
    errors = changeset.errors
    valid = changeset.valid?
    case insert(changeset) do
      :ok, product ->
        conn
          |> put_resp_content_type("application/json")
          |> send_resp(200, Poison.encode!(%
              successs: "success"
          ))
      :error, changeset ->

        conn
          |> put_resp_content_type("application/json")
          |> send_resp(500, Poison.encode!(%
              failure: changeset
          ))
    end
  end

product.ex

defmodule Api.Product do
  use Ecto.Schema

  @derive Poison.Encoder, only: [:name, :brand, :description, :image, :rating, :numberOfVotes]
  schema "products" do
    field :name, :string
    field :brand, :string
    field :description, :string
    field :image, :string
    field :rating, :integer
    field :numberOfVotes, :integer
  end

  def changeset(product, params \\ %) do
    product
    |> Ecto.Changeset.cast(params, [:name, :brand, :description, :image, :rating, :numberOfVotes])
    |> Ecto.Changeset.validate_required([:name, :description, :brand])
  end
end

顺便说一句-origin is evil.com because of a browser plugin I use to enable CORS

【问题讨论】:

insertProduct 函数体的第一行是IO.puts conn。问题是 conn 是一个结构,而 IO.puts/2 接受一个字符串参数。如果您尝试调试字符串或字符数据以外的其他内容,则可以将其切换为 IO.inspect @stevelove 太棒了,谢谢你帮了大忙。已更新问题。 答案就在错误中:expected params to be a map, got: %Api.Product...。可能在这里:lib/api/product.ex:16。当函数期望您传递映射时,您似乎正在调用 Ecto 的 cast 函数之一(即%Api.Product),即% @stevelove 是的,谢谢。会让它期待%Api.Product... 我认为问题出在 repo.ex 中,您正在从 params 创建一个 Product 结构并将其发送到 Api.Product.changeset。直接发送params 【参考方案1】:

在 react native 中使用的一些 javascript 全局常量允许网络请求显示在 chrome 开发者控制台中:

GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest

似乎自己也弄乱了网络请求。一旦我摆脱了那个代码,它就会将POSTed 产品插入到数据库中。

感谢 cmets 帮助我度过了难关。

【讨论】:

以上是关于AJAX Post 请求错误:“不能为空”,[验证::required] 在 POST 正文中发送 JSON 对象时的主要内容,如果未能解决你的问题,请参考以下文章

错误:“CSRF 验证失败。请求中止。”在 Django 中使用 jquery ajax 时

前端ajax中运用post请求和get请求之于session验证

AJAX 发布致命错误列不能为空

CSRF错误Django ajax .post没有表格

php错误fopen()“文件名不能为空”

Laravel API ajax 请求未通过身份验证