I'm using Poison to encode a map to JSON that will send it to the Slack API. This is what Poison gives me:
"{\"text\":\"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296\"}"
When I put that into JSON lint it says it is valid JSON, but Slack responds "invalid payload".
If I change the JSON to look like this
{"text":"changed readme fad996e98e04fd4a861840d92bdcbbcb1e1ec296"}
Then it works. Does anyone know where I'm going wrong with this? Do I need to do extra processing on the encoded JSON or is there some header I need to set?
Here is my controller
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
Here is part of the library for sending the messages
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
And my HTTPoison processing
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
The part that creates the JSON is in the last block.
The default encoding is UTF-8. (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used.
The decoder functionality lets you read JSON data, navigate the objects and arrays within the data, and read and get metadata about individual values. This saves you from writing a parser to read and store complex, multi-layered data. The encoder functionality lets you write JSON data in a field-by-field manner.
Poison is a new JSON library for Elixir focusing on wicked-fast speed without sacrificing simplicity, completeness, or correctness. Poison takes several approaches to be the fastest JSON library for Elixir.
It seems your request payload is JSON encoded twice:
At first it returns the output string {"text":"..."}
and then that string is encoded again. JSON can not only encode objects, but also strings so the encoding the above again will give an output of "{\"text\":\"...\"}"
.
That is, a string that contains the JSON encoded representation of an object.
However, the Slack API expects an object of the form {"text": "..."}
, not a string "..."
. The latter is still valid JSON, but it is not a valid request as far as the API is concerned. That is why you get the “invalid payload” error back.
Where I’m not entirely sure is where the object is encoded a second time. Your code above looks okay, but maybe there are other processing steps that are not in those code snippets. I would suggest you carefully examine your code for a path where two Poison.encode!
calls are applied to some data.
There is, however, a workaround for this – although I highly recommend you find and fix the root cause, the double JSON encoding. In process_request_body/1
you could match the argument – and if it is already a string, skip the Poison.encode!
. To do so, use the following code:
def process_request_body(body) when is_binary(body), do: body
def process_request_body(body)
body
|> Poison.encode! # Turns map into JSON
end
This will pass strings through verbatim and JSON encode anything else. However, this may be dangerous because a string is still a valid input for JSON encoding, so you have to be careful if you want to send pure JSON encoded strings over the API. Again, I recommend fixing the root cause, but depending on your situation, this workaround might also be viable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With