How to access raw request body in servant application

591 Views Asked by At

In a servant/wai application the request body can be obtained using a combinator e.g. ReqBody '[JSON] Book. In this case the body is extracted as a value of type Book. It is not clear how the raw request body can be accessed without converting it to a type.

The raw request body may be required to verify its signature. An example is in stripe webhooks (i.e. stripe.com) where the raw request body may be needed for verification. There is a nice library on hackage.org (stripe-hs) that does this verification but does not explain how to obtain the raw request body.

I guess one way is through a middleware where the request body can be consumed once. Is there any other way?

2

There are 2 best solutions below

0
On

What I ended up doing is to create a new data type data WebhookJSON and its corresponding MimeUnrender and MimeRender instances for this data type. Just like its done for the JSON type. I used that data type in the combinators e.g. ReqBody '[WebhookJSON] ByteString. Hopefully (I haven't tested it yet), this is one way to gain access to the raw request body.

0
On

To add onto 7puns' self-answer:

module JsonAsRawText where

import ClassyPrelude
import qualified Network.HTTP.Media               as M
import qualified Data.Text                        as TextS
import qualified Data.Text.Encoding               as TextS
import qualified Data.List.NonEmpty               as NE
import           Control.Arrow (left)
import Servant

data JsonAsRawText deriving Typeable

instance Accept JsonAsRawText where
  contentTypes _ =
    "application" M.// "json" M./: ("charset", "utf-8") NE.:|
    [ "application" M.// "json" ]

instance MimeUnrender JsonAsRawText TextS.Text where
  mimeUnrender _ = left show . TextS.decodeUtf8' . toStrict

It's worth noting that this bypasses a lot of Servant's wonderful automatic type machinery. It's really only useful when you absolutely need access to the raw body of a request of Content-Type: application/json as a string (to compute the security signature for an incoming webhook, for instance).