How to consume a conduit with WAI (raw request body)
-
03-12-2019 - |
سؤال
I'm using scotty, which is a sinatra-like wrapper around WAI. I want to get the raw request body as a byte string so I can parse it as json. The following is close. This is similar to other questions about consuming a body using WAI, but is different because I want the body as a bytestring, and because I'm in a different monad, ActionM
import Network.Wai (requestBody)
import Web.Scotty (ActionM, request, text)
bodyExample :: ActionM ()
bodyExample = do
r <- request
bss <- requestBody r -- this needs a lift or something
text "ok"
...
It obviously won't work, I think I need some kind of lift or something, but I don't know what to use. liftIO
isn't right, and lift
gives me weird errors.
http://hackage.haskell.org/packages/archive/scotty/0.0.1/doc/html/Web-Scotty.html
http://hackage.haskell.org/packages/archive/wai/latest/doc/html/Network-Wai.html
المحلول
requestBody
isn't a monadic value. It is simply a function that returns a Conduit Source IO ByteString
.
To consume the source, use Data.Conduit.List.consume
(or Data.Conduit.Lazy.lazyConsume
). You will get a list of ByteString
s as the result. To then exit the ResourceT
monad transformer, use runResourceT
. The resulting code:
bss <- liftIO . runResourceT . lazyConsume . requestBody $ r
bss :: [ByteString]
نصائح أخرى
For what it's worth, the new version of Scotty (0.2.0) has a 'jsonData' method to do this for you. Thanks for using!
The accepted answer won't actually work due to the way lazyConsume
works. It will always return an empty list. You need to consume the data before exiting ResourceT
if you're using lazyConsume
.
As an alternative, here's how to strictly consume the bytestring and return it:
rawRequestBody :: Request -> IO B.ByteString
rawRequestBody req = mconcat <$> runResourceT (requestBody req $$ consume)
This is the code that finally work for me, adapted from jhickner's
rawRequestBody req = mconcat <$> (requestBody req $$ consume)