如何在 Absinthe GraphQL 请求中接受 JSON

Posted

技术标签:

【中文标题】如何在 Absinthe GraphQL 请求中接受 JSON【英文标题】:How to accept JSON in Absinthe GraphQL requests 【发布时间】:2019-06-01 15:59:06 【问题描述】:

我试图在我的 graphql 实现中接收 JSON 字符串,但我为处理 JSON 而定义的自定义标量不断出现错误。

我已经定义了一个自定义标量来正确地将 JSON 序列化为 elixir 映射。在我的代码到达自定义标量的解析阶段之前,我收到错误数据类型无效。我正在尝试使用 https://github.com/absinthe-graphql/absinthe/wiki/Scalar-Recipes#json-using-jason 创建标量,但我已修改为使用 Poison 而不是 Jason。

我使用我创建的 :json 标量类型的苦艾变体。

@desc "Update user"
field :update_user, type: :user do
  arg(:email, :string)
  arg(:password, :string)
  arg(:first_name, :string)
  arg(:last_name, :string)
  arg(:age, :integer)
  arg(:client_store, :json)

  resolve(handle_errors(&Resolvers.User_Resolver.update_user/3))
end

我的标量定义和 graphql 模式定义

    scalar :json, name: "Json" do
        description("""
        The `Json` scalar type represents arbitrary json string data, represented as UTF-8
        character sequences. The Json type is most often used to represent a free-form
        human-readable json string.
        """)
        serialize(&encode/1)
        parse(&decode/1)
    end

    # @spec decode(Absinthe.Blueprint.Input.String.t) :: :ok, :string | :error
    # @spec decode(Absinthe.Blueprint.Input.Null.t) :: :ok, nil
    defp decode(%Absinthe.Blueprint.Input.Stringvalue: value) do
        Logger.info "decoded input value:"
        case Poison.decode(value) do
        :ok, result -> :ok, result
        _ -> :error
        end
    end

    defp decode(%Absinthe.Blueprint.Input.Null) do
        :ok, nil
    end

    defp decode(_) do
        :error
    end

    defp encode(value), do: value

    object :user do
        field(:id, :id)
        field(:email, :string)
        field(:password, :string)
        field(:first_name, :string)
        field(:last_name, :string)
        field(:age, :integer)
        field(:client_store, :json)
    end

发送以下查询时:

    mutation updateUser
      updateUser(client_store: ""key":"value"")
        id
      
    

我收到了syntax error before: \"\\\":\\\"\"

    mutation updateUser
      updateUser(client_store: "hello")
        id
      
    

我收到了"Argument \"client_store\" has invalid value \"hello\"

通过 Phoenix.ConnTest 的单元测试发送 GraphQL 查询

query = """
    mutation updateUser
      updateUser(client_store: "\"key\":\"value\"")
        id
      
    
  """

res =
  context.conn
  |> put_req_header("content-type", "text")
  |> put_req_header("authorization", token)
  |> post("/api", query)

【问题讨论】:

【参考方案1】:

问题在于您的突变,您需要转义字符串中的引号,如下所示:

mutation updateUser
  updateUser(client_store: "\"key\": \"value\"")
    id
  

否则,解释是第二个引号标记了字符串的结尾,所以你基本上有字符串""后跟key":"value"",这在语法上是无效的。

请注意,在单元测试中发送突变时,您已经将突变定义为字符串,因此您需要进一步转义引号:

query = """
    mutation updateUser
      updateUser(client_store: "\\\"key\\\":\\\"value\\\"")
        id
      
    
  """

一开始可能有点难以理解,但打印出来会更清楚:

iex(1)> """
...(1)>     mutation updateUser
...(1)>       updateUser(client_store: "\"key\":\"value\"")
...(1)>         id
...(1)>       
...(1)>     
...(1)> """ |> IO.puts()
    mutation updateUser
      updateUser(client_store: ""key":"value"")
        id
      
    

iex(2)> """
...(2)>     mutation updateUser
...(2)>       updateUser(client_store: "\\\"key\\\":\\\"value\\\"")
...(2)>         id
...(2)>       
...(2)>     
...(2)> """ |> IO.puts() 
    mutation updateUser
      updateUser(client_store: "\"key\":\"value\"")
        id
      
    

所以最后一个有效,因为在评估字符串时,结果是原始突变,它转义了引号。

【讨论】:

不幸的是,运行 mutation updateUser updateUser(client_store: "\"key\": \"value\"") id 产生:syntax error before: \"\\\": \\\"\" 你是如何发送突变的?通过 GraphiQL? 在单元测试中通过Phoenix.ConnTest 发送。我将包括我发送问题的方式以提高知名度。 好的,我已经通过 GraphiQL 让它工作了。不确定单元测试有什么问题。完成后我会发布。感谢您的帮助。 我已经编辑了我的答案以解决您在单元测试中遇到的问题。【参考方案2】:

我是这样测试的

  test "should get an existing key" do
    :ok, user = Api.Auth.Users.create_user(%name: "Test User")
    :ok, app = Api.Auth.Apps.create_app(%owner_id: %type: :user, id: user.id, name: "name")
    :ok, key = Api.Auth.Keys.create_key(%app_id: app.id)

    %resp_body: response =
      build_conn()
      |> post("graphql", %
        "query" => """
          query 
            key(id: "#key.id") 
              id
            
          
        """
      )

    %"data" => %"key" => %"id" => id = Jason.decode!(response)

    assert key.id == id
  end
end

【讨论】:

以上是关于如何在 Absinthe GraphQL 请求中接受 JSON的主要内容,如果未能解决你的问题,请参考以下文章

Absinthe Graphql 嵌套查询安全性

为啥我的 Absinthe GraphQL 订阅不起作用?

Elixir Absinthe GraphQL,联合 resolve_type list_of/1

是否可以使用Elixir的Absinthe插件和Neo4J

如何在所有 GraphQL 请求的标头中注入 Id。例如,在所有 GraphQL 请求的标头中,它应该像:- Id:1234

如何在 GraphQL 中包含突变请求元数据?