@@ -66,15 +66,14 @@ module Servant.API.ContentTypes
66 , AllMime(..)
66 , AllMime(..)
67 , AllMimeRender(..)
67 , AllMimeRender(..)
68 , AllMimeUnrender(..)
68 , AllMimeUnrender(..)
69− , FromFormUrlEncoded(..)
70− , ToFormUrlEncoded(..)
71 , eitherDecodeLenient
69 , eitherDecodeLenient
72 , canHandleAcceptH
70 , canHandleAcceptH
75import Control.Arrow (left)
73import Control.Arrow (left)
76import Control.Monad.Compat
74import Control.Monad.Compat
77−import Data.Aeson (FromJSON(..), ToJSON(..), encode)
75+import Data.Aeson (FromJSON (..), ToJSON (..),
78import Data.Aeson.Parser (value)
77import Data.Aeson.Parser (value)
79import Data.Aeson.Types (parseEither)
78import Data.Aeson.Types (parseEither)
80import Data.Attoparsec.ByteString.Char8 (endOfInput, parseOnly,
79import Data.Attoparsec.ByteString.Char8 (endOfInput, parseOnly,
@@ -82,10 +81,8 @@ import Data.Attoparsec.ByteString.Char8 (endOfInput, parseOnly,
82import qualified Data.ByteString as BS
81import qualified Data.ByteString as BS
83import Data.ByteString.Lazy (ByteString, fromStrict,
82import Data.ByteString.Lazy (ByteString, fromStrict,
85−import qualified Data.ByteString.Lazy as B
86import qualified Data.ByteString.Lazy.Char8 as BC
84import qualified Data.ByteString.Lazy.Char8 as BC
87import Data.Maybe (isJust)
85import Data.Maybe (isJust)
88−import Data.Monoid.Compat
89import Data.String.Conversions (cs)
86import Data.String.Conversions (cs)
90import qualified Data.Text as TextS
87import qualified Data.Text as TextS
91import qualified Data.Text.Encoding as TextS
88import qualified Data.Text.Encoding as TextS
@@ -94,8 +91,9 @@ import qualified Data.Text.Lazy.Encoding as TextL
94import Data.Typeable
91import Data.Typeable
95import GHC.Generics (Generic)
92import GHC.Generics (Generic)
96import qualified Network.HTTP.Media as M
93import qualified Network.HTTP.Media as M
97−import Network.URI (escapeURIString,
94+import Web.FormUrlEncoded (FromForm, ToForm,
98− isUnreserved, unEscapeString)
95+ urlEncodeAsForm,
99import Prelude ()
97import Prelude ()
100import Prelude.Compat
98import Prelude.Compat
@@ -290,12 +288,12 @@ instance OVERLAPPABLE_
290 ToJSON a => MimeRender JSON a where
288 ToJSON a => MimeRender JSON a where
291 mimeRender _ = encode
289 mimeRender _ = encode
293−-- | @encodeFormUrlEncoded . toFormUrlEncoded@
291+-- | @urlEncodeAsForm"
294-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
292-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
295-- holds if every element of x is non-null (i.e., not @("", "")@)
293-- holds if every element of x is non-null (i.e., not @("", "")@)
296instance OVERLAPPABLE_
294instance OVERLAPPABLE_
297− ToFormUrlEncoded a => MimeRender FormUrlEncoded a where
295+ ToForm a => MimeRender FormUrlEncoded a where
298− mimeRender _ = encodeFormUrlEncoded . toFormUrlEncoded
296+ mimeRender _ = urlEncodeAsForm
300-- | `TextL.encodeUtf8`
298-- | `TextL.encodeUtf8`
301instance MimeRender PlainText TextL.Text where
299instance MimeRender PlainText TextL.Text where
@@ -348,11 +346,11 @@ eitherDecodeLenient input =
348instance FromJSON a => MimeUnrender JSON a where
346instance FromJSON a => MimeUnrender JSON a where
349 mimeUnrender _ = eitherDecodeLenient
347 mimeUnrender _ = eitherDecodeLenient
351−-- | @decodeFormUrlEncoded >=> fromFormUrlEncoded@
349+-- | @urlDecodeAsForm@
352-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
350-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
353-- holds if every element of x is non-null (i.e., not @("", "")@)
351-- holds if every element of x is non-null (i.e., not @("", "")@)
354−instance FromFormUrlEncoded a => MimeUnrender FormUrlEncoded a where
352+instance FromForm a => MimeUnrender FormUrlEncoded a where
355− mimeUnrender _ = decodeFormUrlEncoded >=> fromFormUrlEncoded
353+ mimeUnrender _ = left TextS.unpack . urlDecodeAsForm
357-- | @left show . TextL.decodeUtf8'@
355-- | @left show . TextL.decodeUtf8'@
358instance MimeUnrender PlainText TextL.Text where
356instance MimeUnrender PlainText TextL.Text where
@@ -375,49 +373,6 @@ instance MimeUnrender OctetStream BS.ByteString where
375 mimeUnrender _ = Right . toStrict
373 mimeUnrender _ = Right . toStrict
378−--------------------------------------------------------------------------
381−-- | A type that can be converted to @application/x-www-form-urlencoded@
382−class ToFormUrlEncoded a where
383− toFormUrlEncoded :: a -> [(TextS.Text, TextS.Text)]
385−instance ToFormUrlEncoded [(TextS.Text, TextS.Text)] where
386− toFormUrlEncoded = id
388−-- | A type that can be converted from @application/x-www-form-urlencoded@,
389−-- with the possibility of failure.
390−class FromFormUrlEncoded a where
391− fromFormUrlEncoded :: [(TextS.Text, TextS.Text)] -> Either String a
393−instance FromFormUrlEncoded [(TextS.Text, TextS.Text)] where
394− fromFormUrlEncoded = return
396−encodeFormUrlEncoded :: [(TextS.Text, TextS.Text)] -> ByteString
397−encodeFormUrlEncoded xs =
398− let escape :: TextS.Text -> ByteString
399− escape = cs . escapeURIString isUnreserved . cs
400− encodePair :: (TextS.Text, TextS.Text) -> ByteString
401− encodePair (k, "") = escape k
402− encodePair (k, v) = escape k <> "=" <> escape v
403− in B.intercalate "&" $ map encodePair xs
405−decodeFormUrlEncoded :: ByteString -> Either String [(TextS.Text, TextS.Text)]
406−decodeFormUrlEncoded "" = return []
407−decodeFormUrlEncoded q = do
408− let xs :: [TextS.Text]
409− xs = TextS.splitOn "&" . cs $ q
410− parsePair :: TextS.Text -> Either String (TextS.Text, TextS.Text)
412− case TextS.splitOn "=" p of
413− [k,v] -> return ( unescape k
416− [k] -> return ( unescape k, "" )
417− _ -> Left $ "not a valid pair: " <> cs p
418− unescape :: TextS.Text -> TextS.Text
419− unescape = cs . unEscapeString . cs . TextS.intercalate "%20" . TextS.splitOn "+"
423-- >>> import Servant.API
378-- >>> import Servant.API