Reputation: 7327
Problem: I am attempting to POST some JSON to an HTTP endpoint that only accepts Base64 encoding.
Code Sample: Here is a code sample which successfully posts without Base64 encoding:
{-# LANGUAGE OverloadedStrings #-}
module Lib where
import Data.Aeson (encode, object, (.=))
import qualified Data.ByteString.Lazy.Char8 as L8
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import Network.HTTP.Types.Status (statusCode)
import qualified Data.ByteString.Base64 as B64
postJSON :: IO ()
postJSON = do
manager <- newManager tlsManagerSettings
-- Nested JSON object to POST:
let requestObject = object
[ "event" .= ("App launched." :: String)
, "properties" .= object [ "distinct_id" .= ("user" :: String)
, "token" .= ("f793bae9548d8e123cef251fd81df487" :: String)
]
]
initialRequest <- parseRequest "http://api.mixpanel.com/track"
let request = initialRequest
{ method = "POST"
, requestBody = RequestBodyLBS $ encode requestObject
, requestHeaders =
[ ("Content-Type", "application/json; charset=utf-8")
]
}
response <- httpLbs request manager
putStrLn $ "The status code was: "
++ show (statusCode $ responseStatus response)
L8.putStrLn $ responseBody response
Attempt: In order to send the JSON as Base64 encoded, I tried replacing requestBody = RequestBodyLBS $ encode requestObject
with requestBody = RequestBodyLBS $ Data.Bytestring.Base64.encode (encode requestObject)
, but I get a type error. So how do I encode the JSON as Base64 for this HTTP POST?
Upvotes: 0
Views: 893
Reputation: 1253
I have two things to add to jberryman's answer.
First, if (as it now appears) you're going to be putting this in a query string, you need to make sure you don't just use a base64 encoded bytestring, but instead use a base64 url-encoded bytestring. So don't use Data.Bytestring.Base64
(as jberryman linked to), but rather Data.Bytestring.Base64.URL
(here).
Second, while he pointed you in the right direction on the Base64 encoding part, it seems you're still hung up on setting the querystring. For that, you should check out the setQueryString
function in the http-client
library you're already using (link here).
That function has the signature:
setQueryString :: [(ByteString, Maybe ByteString)] -> Request -> Request
So if you're base64 encoded bytestring is built like this
let urlEncodedBytestring = Data.Bytestring.Base64.URL.encode . L8.toStrict $ encode requestObject
and if you're attemtping to set the data
key in the querystring of your request, then you'll probably want:
let requestWithQueryStringSet = setQueryString [("data", (Just urlEncodedBytestring))] request
Upvotes: 2
Reputation: 16645
B64.encode
here is a function from strict ByteString
to strict ByteString
(you'll need to hover over the type name in the haddocks to see this if you're just browsing), while Aeson.encode
returns a lazy bytestring (from the Data.ByteString.Lazy
module). These are two distinct types although they have the same name.
You probably have to do something like:
...
requestBody = RequestBodyLBS $ L8.fromStrict $ Data.Bytestring.Base64.encode (L8.toStrict $ encode requestObject)
Upvotes: 3