Elixir adds an extra slash in POST request body, making a URL inside it invalid

The approximate code:

    params = %{query: "https://www.lol.com"}
    params = Jason.encode!(params)
    HTTPoison.post(url, params, headers, @options)

The approximate request and response:

[
  {
    "request": {
      "body": "{\"query\":\"https://lol.com\"}",
      "url": "https://url.com",
      "headers": {
        "Authorization": "***",
        "Content-Type": "application/json"
      },
      "method": "post",
      "request_body": ""
    },
    "response": {
      "binary": false,
      "type": "ok",
      "body": "{\"message\":\"Bad Request\"}\n",
      "headers": {
        "content-type": "application/json",
        "content-length": "26"
      },
      "status_code": 400
    }
  }
]

Inspecting the body also returns "{\"query\":\"https://lol.com\"}"

As far as I can tell, it’s from the extra slash that comes after .com to escape the double quote.

What I’ve tried:
Building the body in a sigil like these:
~S({"query":"https://lol.com"})
~s<{"query":"https://lol.com"}>

Replacing the slash with String.replace

Using req instead of HTTPoison

This is a 3rd party API so I don’t have control over how their server receives it. Any further ideas for a client-side solution (the client being my Elixir code)?

I can get a curl request working with all the same params, but I can’t make it work in Elixir.

These seem to fail because Elixir always represents JSON object strings with escaped quotes.

curl 'https://url.com' 'Content-Type: application/json' -d '{\n "query": "https://lol.com"    }

First difference I see is the UA.

So try setting a UA that is close to what curl does.

This is odd, because those are just representational.

If you would do File.write("test.txt", Jason.encode(%{query: "https://www.lol.com"}), you would see no backslashes.

If I take your example of above:

"{\"query\":\"https://lol.com\"}" |> String.length()
"{'query':'https://lol.com'}" |> String.length()
Both of these will return 27.

So I don’t think this is your issue.

2 Likes

PS: To make one thing clear, I do not see an “extra” slash (/) anywhere, only some backslash (\) which often is confusing.

Please make sure to understand that escaping it is a purely representational thing.

iex(1)> "{\"query\":\"https://lol.com\"}" == ~s<{"query":"https://lol.com"}>
true

Can you show the full curl command that works?

Thank you everyone for your patience and your responses.

The issue was not the backslash (which I incorrectly called a slash) as you pointed out. The issue was that this request required Basic Auth, which evidently must be Base64 encoded in order to work. I was sending the auth string in plaintext.

Problem solved. Thank you!

2 Likes