Кодирование карты JSON с использованием Poison для использования с Slack

Я использую Poison для кодирования карты JSON, которая отправит ее в Slack API. Это то, что дает Яд:

"{\"text\":\"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296\"}"

Когда я помещал это в JSON lint, он говорит, что он действителен JSON, но Slack отвечает на "недопустимую полезную нагрузку".

Если я изменил JSON, чтобы выглядеть так:

{"text":"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296"}

Тогда он работает. Кто-нибудь знает, где я ошибаюсь? Нужно ли делать дополнительную обработку на кодированном JSON или есть какой-то заголовок, который мне нужно установить?

Вот мой контроллер

def create(conn, opts) do
    message = Message.create_struct(opts)
    response = Slack.Messages.send(message)

    case response do
      {:ok, data} ->
        render conn, json: Poison.encode!(data)
      {:error, reason} ->
        render conn, json: reason
    end
end

Вот часть библиотеки для отправки сообщений

defmodule Slack.Messages do

  def format_simple_message(map) do
    text = map.description <> " " <> map.commits
    message = %{text: text}
  end

  def post_to_slack(map) do
    Slack.post(:empty, map)
  end

  def send(map) do
    map
    |> format_simple_message
    |> post_to_slack
  end

end

И моя обработка HTTPoison

defmodule Slack do
  use HTTPoison.Base

  @endpoint "http://url.com"

  def process_url() do
    @endpoint
  end

  def process_response_body(body) do
    body
    |> Poison.decode! # Turns JSON into map
  end

  def process_request_body(body) do
    body
    |> Poison.encode! # Turns map into JSON
  end
end

Часть, которая создает JSON, находится в последнем блоке.

Ответ 1

Кажется, ваша полезная нагрузка вашего запроса JSON кодируется дважды: Сначала он возвращает строку вывода {"text":"..."}, а затем эта строка снова закодирована. JSON может не только кодировать объекты, но также строки, поэтому кодировка вышеописанного даст результат "{\"text\":\"...\"}". То есть, строка, содержащая кодированное JSON представление объекта.

Однако Slack API ожидает объект формы {"text": "..."}, а не строку "...". Последний по-прежнему действителен JSON, но он не является допустимым запросом в отношении API. Вот почему вы возвращаете ошибку "неправильной полезной нагрузки".

Где Im не совсем уверен, где объект закодирован второй раз. Ваш код выше выглядит нормально, но, возможно, есть другие этапы обработки, которые не содержатся в этих фрагментах кода. Я бы посоветовал вам внимательно изучить ваш код для пути, где к некоторым данным применяются два вызова Poison.encode!.

Однако для этого существует обходной путь - хотя я настоятельно рекомендую вам найти и исправить первопричину, двойную кодировку JSON. В process_request_body/1 вы можете сопоставить аргумент - и если это уже строка, пропустите Poison.encode!. Для этого используйте следующий код:

def process_request_body(body) when is_binary(body), do: body
def process_request_body(body)
  body
  |> Poison.encode! # Turns map into JSON
end

Это передаст строки через дословно и JSON закодирует что-нибудь еще. Однако это может быть опасно, потому что строка по-прежнему является допустимым входом для кодирования JSON, поэтому вам нужно быть осторожным, если вы хотите отправлять чистые строки JSON, закодированные по API. Опять же, я рекомендую установить основную причину, но в зависимости от вашей ситуации это обходное решение также может быть жизнеспособным.