I have two http-servers working with a json api using the snap framework
my first prototype contains a handler similar to this example handler
import Data.ByteString (ByteString)
import Data.ByteString.Char8 as B (unwords, putStrLn)
import Data.ByteString.Lazy.Char8 as L (putStrLn)
import Control.Monad.IO.Class (liftIO)
import Data.Monoid ((<>))
import Snap.Core (getParam, modifyResponse, setHeader, writeLBS)
import Network.HTTP.Conduit
import Network.HTTP.Client (defaultManagerSettings)
exampleHandler :: AppHandler ()
exampleHandler = do resp <- liftIO
$ do L.putStrLn "Begin request ..."
initReq <- parseUrl "http://localhost:8001/api"
manager <- newManager defaultManagerSettings
let req = initReq { method = "GET"
, proxy = Nothing}
r <- httpLbs req manager
L.putStrLn "... finished request."
return $ responseBody r
liftIO . L.putStrLn $ "resp: " <> resp
modifyResponse $ setHeader "Content-Type" "application/json"
writeLBS $ "{ \"data\": \""<> resp <>"\" }"
If I issue an ajax-request, the response is sent and received - i see this when the server writes resp: testdata
on the console, but the response sent to the browser with writeLBS
is not. Now if I change the last line to
writeLBS $ "{ \"data\": \""<> "something fixed" <>"\" }"
everything works like a charm. I think I am meeting one of the pitfalls of lazy IO, but I don't know how to remedy this.
I also tried a few variations with no singe liftIO
-block but putting liftIO
where necessary.
based on the comment by @MichaelSnoyman I did some research regarding writeLBS
and tried to
modifyResponse $ setBufferingMode False
. setHeader "Content-Type" "application/json"
writeLBS resp
as I thought maybe buffering could be the problem - no it is not
Furthermore I tried to write explicitly a setResponseBody
let bb = enumBuilder . fromLazyByteString $ "{ \"data\": \""<> resp <>"\" }"
modifyResponse $ setBufferingMode False
. setHeader "Content-Type" "application/json"
. setResponseBody bb
Which showed also no success.
I have solved this issue - it actually was a problem with the javascript getting the handwritten json (note to self: never do that again). There was a non-breaking space at the end of the input data that was not encoded correctly, and I as I am a newbie at JS I didn't get that from the error message.
The intermediate solution is to add urlEncode
and make a strict ByteString
let respB = urlEncode . L.toStrict $ C.responseBody resp
modifyResponse $ setBufferingMode False
. setHeader "Content-Type" "application/json"
writeBS $ "{ \"data\": \"" <> respB <> "\" }"
of course you have to change imports accordingly.
The long term solution is: write a proper from/toJSON
instance and let the library deal with this.