{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
module Network.Wai.Routing.Purescheme.Core.Entity.Json
( withContentNegotiationJson
, entityJson
)
where
import Network.Wai.Routing.Purescheme.Core.Basic
import Network.Wai.Routing.Purescheme.Core.Entity
import Network.Wai.Routing.Purescheme.Core.Internal
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as LBS
import Data.String.Interpolate.IsString (i)
import qualified Data.Text as T
import Network.HTTP.Media (MediaType, matchContent)
import Network.HTTP.Types (unsupportedMediaType415, badRequest400, statusMessage, hContentType)
import Network.Wai (Response, strictRequestBody, requestHeaders)
import Data.Aeson (ToJSON, FromJSON, eitherDecode, encode)
withContentNegotiationJson :: ((forall a. ToJSON a => EntityResponse a -> NegotiatedResponse) -> GenericApplication NegotiatedResponse) -> GenericApplication Response
withContentNegotiationJson f = withCustomNegotiation $ f $ negotiated negotiationJson
negotiationJson :: ToJSON a => [(ByteString, a -> LBS.ByteString)]
negotiationJson =
[ ("application/json", encode)
]
entityJson :: FromJSON a => (a -> GenericApplication b) -> GenericApplication b
entityJson inner req respond =
if isValidContentType
then do
valueString <- strictRequestBody req
case eitherDecode valueString of
Right result -> inner result req respond
Left decodeError -> reject' $ decodeErrorRejection $ T.pack decodeError
else reject' $ unsupportedMediaTypeRejection "Content-Type not supported"
where
isValidContentType =
case lookup hContentType $ requestHeaders req of
Nothing -> True
Just contentTypeHeader -> case matchContent ["application/json" :: MediaType] contentTypeHeader of
Nothing -> False
Just _ -> True
unsupportedMediaTypeRejection :: T.Text -> Rejection
unsupportedMediaTypeRejection errorMessage =
Rejection
{ status = unsupportedMediaType415
, message = [i|#{statusMessage unsupportedMediaType415}: #{errorMessage}|]
, priority = 200
, headers = []
}
decodeErrorRejection :: T.Text -> Rejection
decodeErrorRejection errorMessage =
Rejection
{ status = badRequest400
, message = [i|#{statusMessage badRequest400}: Error decoding entity body: #{errorMessage}|]
, priority = 300
, headers = []
}