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

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

"{\"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, находится в последнем блоке.


person humdinger    schedule 16.07.2016    source источник
comment
Можете ли вы опубликовать источник соответствующей функции контроллера? Вероятно, вы звоните json(conn, Poison.encode!(data)) вместо json(conn, data).   -  person Dogbert    schedule 16.07.2016
comment
Добавил немного кода, чтобы лучше понять, что я делаю.   -  person humdinger    schedule 17.07.2016
comment
Вот что дает мне Poison: ... откуда в коде вы взяли это значение?   -  person Dogbert    schedule 17.07.2016
comment
Это происходит каждый раз, когда я кодирую с помощью Poison. Это конкретно из process_request_body. В документации Poison так выглядит вывод в его примере в разделе использования: github.com/devinus/poison   -  person humdinger    schedule 17.07.2016
comment
Какую конечную точку вы пытаетесь достичь, входящий URL-адрес веб-перехватчика или вы используете конечную точку chat.postMessage API? Первый принимает application/json (но вы должны убедиться, что установлен HTTP-заголовок Content-type: application/json), а второй не поддерживает POST-сообщения на основе JSON таким образом.   -  person Taylor Singletary    schedule 26.07.2016
comment
Попробуйте JSON.parse для возвращенного объекта из карты кодирования Poison в JSON, а затем отправьте его в слабый API?   -  person Indra Uprade    schedule 27.07.2016


Ответы (1)


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

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

Где я не совсем уверен, так это в том, где объект кодируется во второй раз. Ваш код выше выглядит нормально, но, возможно, есть другие шаги обработки, которых нет в этих фрагментах кода. Я бы посоветовал вам внимательно изучить свой код на наличие пути, в котором к некоторым данным применяются два вызова 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. Опять же, я рекомендую устранить основную причину, но в зависимости от вашей ситуации этот обходной путь также может быть жизнеспособным.

person user1449556    schedule 27.09.2016