1

我正在使用苦艾酒并且有一个突变的迹象。当用户通过有效凭据发送时,我想通过在响应中设置会话 cookie put_session

我面临的问题是我无法conn从解析器函数中访问。这告诉我,我不应该从解析器中更新连接的属性。

用苦艾酒可以做到这一点吗?有哪些替代解决方案?

4

2 回答 2

3

看起来一种解决方案是:

  1. 在解析器中,正常解析 an{:ok, _}或 an{:error, _}
  2. 在解析器之后添加中间件以从步骤 1 返回的模式匹配resolution.value并更新 GraphQL 上下文
  3. 使用 Absinthe 的before_sendput_session功能(在发送响应之前可以访问 GraphQL 上下文和连接

代码示例

突变:

mutation do
  @desc "Authenticate a user."
  field :login, :user do
    arg(:email, non_null(:string))
    arg(:password, non_null(:string))
    resolve(&Resolvers.Accounts.signin/3)

    middleware(fn resolution, _ ->
      case resolution.value do
        %{user: user, auth_token: auth_token} ->
          Map.update!(
            resolution,
            :context,
            &Map.merge(&1, %{auth_token: auth_token, user: user})
          )

        _ ->
          resolution
      end
    end)
  end
end

解析器:

defmodule AppWeb.Resolvers.Accounts do
  alias App.Accounts

  def signin(_, %{email: email, password: password}, _) do
    if user = Accounts.get_user_by_email_and_password(email, password) do
      auth_token = Accounts.generate_user_session_token(user)
      {:ok, %{user: user, auth_token: auth_token}}
    else
      {:error, "Invalid credentials."}
    end
  end
end

路由器:

defmodule AppWeb.Router do
  use AppWeb, :router

  pipeline :api do
    plug(:accepts, ["json"])
    plug(:fetch_session)
  end

  scope "/" do
    pipe_through(:api)

    forward("/api", Absinthe.Plug,
      schema: AppWeb.Schema,
      before_send: {__MODULE__, :absinthe_before_send}
    )

    forward("/graphiql", Absinthe.Plug.GraphiQL,
      schema: AppWeb.Schema,
      before_send: {__MODULE__, :absinthe_before_send}
    )
  end

  def absinthe_before_send(conn, %Absinthe.Blueprint{} = blueprint) do
    if auth_token = blueprint.execution.context[:auth_token] do
      put_session(conn, :auth_token, auth_token)
    else
      conn
    end
  end

  def absinthe_before_send(conn, _) do
    conn
  end
end
于 2020-08-27T00:18:17.597 回答
1

不知道为什么要使用会话,这不能使用承载来解决吗?

请忽略接口。:-)

突变。

  object :user_token_payload do
    field(:user, :user)
    field(:token, :string)
  end

  object :login_user_mutation_response, is_type_of: :login_user do
    interface(:straw_hat_mutation_response)

    field(:errors, list_of(:straw_hat_error))
    field(:successful, non_null(:boolean))
    field(:payload, :user_token_payload)
  end

解析器。

  def authenticate_user(args, _) do
    case Accounts.authenticate_user(args) do
      {:ok, user, token} -> MutationResponse.succeeded(%{user: user, token: token})
      {:error, message} -> MutationResponse.failed(StrawHat.Error.new(message))
    end
  end

现在,客户端可以使用 Authorization 标头传递该令牌,并使用插头来获取它。

defmodule MyAppWeb.Plugs.Context do
  import Plug.Conn
  alias MyApp.Admission

  def init(opts), do: opts

  def call(conn, _) do
    case build_context(conn) do
      {:ok, context} -> put_private(conn, :absinthe, %{context: context})
      _ -> put_private(conn, :absinthe, %{context: %{}})
    end
  end

  @doc """
  Return the current user context based on the authorization header
  """
  def build_context(conn) do
    auth_header =
      get_req_header(conn, "authorization")
      |> List.first()

    if auth_header do
      "Bearer " <> token = auth_header

      case Admission.get_token_by_hash(token) do
        nil -> :error
        token -> {:ok, %{current_user: token.user}}
      end
    else
      :error
    end
  end
end

然后将插头添加到您的管道中

plug(MyApp.Plugs.Context)

然后,您可以像这样在解析器中获取当前用户。

  def create_note(%{input: input}, %{context: %{current_user: user}}) do
  end
于 2020-08-27T07:14:32.343 回答