| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Web.FormUrlEncoded
Description
Convert Haskell values to and from application/xxx-form-urlencoded format.
Synopsis
- class ToForm a where
- class FromForm a where
- class ToFormKey k where
- class FromFormKey k where
- parseFormKey :: Text -> Either Text k
- newtype Form = Form {}
- urlEncodeAsForm :: ToForm a => a -> ByteString
- urlEncodeAsFormStable :: ToForm a => a -> ByteString
- urlDecodeAsForm :: FromForm a => ByteString -> Either Text a
- urlEncodeForm :: Form -> ByteString
- urlEncodeFormStable :: Form -> ByteString
- urlDecodeForm :: ByteString -> Either Text Form
- genericToForm :: (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form
- genericFromForm :: (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a
- data FormOptions = FormOptions {
- fieldLabelModifier :: String -> String
- defaultFormOptions :: FormOptions
- toListStable :: Form -> [(Text, Text)]
- toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
- toEntriesByKeyStable :: (Ord k, FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
- fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form
- lookupAll :: Text -> Form -> [Text]
- lookupMaybe :: Text -> Form -> Either Text (Maybe Text)
- lookupUnique :: Text -> Form -> Either Text Text
- parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
- parseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v)
- parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v
- urlEncodeParams :: [(Text, Text)] -> ByteString
- urlDecodeParams :: ByteString -> Either Text [(Text, Text)]
Classes
Convert a value into Form.
An example type and instance:
{-# LANGUAGE OverloadedLists #-}
data Person = Person
{ name :: String
, age :: Int }
instance ToForm Person where
toForm person =
[ ("name", toQueryParam (name person))
, ("age", toQueryParam (age person)) ]
Instead of manually writing instances you can
use a default generic implementation of ToForm.toForm
To do that, simply add deriving clause to your datatype
and declare a GenericToForm instance for your datatype without
giving definition for toForm.
For instance, the previous example can be simplified into this:
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic)
instance ToForm Person
The default implementation of toForm is genericToForm.
Minimal complete definition
Nothing
Methods
Convert a value into Form.
Instances
| ToForm Form # | |
Defined in Web.Internal.FormUrlEncoded | |
| ToHttpApiData v => ToForm (IntMap [v]) # | |
Defined in Web.Internal.FormUrlEncoded | |
| (ToFormKey k, ToHttpApiData v) => ToForm [(k, v)] # | |
Defined in Web.Internal.FormUrlEncoded | |
| (ToFormKey k, ToHttpApiData v) => ToForm (Map k [v]) # | |
Defined in Web.Internal.FormUrlEncoded | |
| (ToFormKey k, ToHttpApiData v) => ToForm (HashMap k [v]) # | |
Defined in Web.Internal.FormUrlEncoded | |
Parse Form into a value.
An example type and instance:
data Person = Person
{ name :: String
, age :: Int }
instance FromForm Person where
fromForm f = Person
<$> parseUnique "name" f
<*> parseUnique "age" f
Instead of manually writing instances you can
use a default generic implementation of FromForm.fromForm
To do that, simply add deriving clause to your datatype
and declare a GenericFromForm instance for your datatype without
giving definition for fromForm.
For instance, the previous example can be simplified into this:
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic)
instance FromForm Person
The default implementation of fromForm is genericFromForm.
It only works for records and it will use parseQueryParam for each field's value.
Minimal complete definition
Nothing
Instances
| FromForm Form # | |
| FromHttpApiData v => FromForm (IntMap [v]) # | |
| (FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] # | _NOTE:_ this conversion is unstable and may result in different key order (but not values). |
| (Ord k, FromFormKey k, FromHttpApiData v) => FromForm (Map k [v]) # | |
| (Eq k, Hashable k, FromFormKey k, FromHttpApiData v) => FromForm (HashMap k [v]) # | |
Keys for Form entries
Instances
class FromFormKey k where #
Instances
Form type
The contents of a form, not yet URL-encoded.
Form can be URL-encoded with urlEncodeForm and URL-decoded with urlDecodeForm.
Instances
| Monoid Form # | |||||
| Semigroup Form # | |||||
| Generic Form # | |||||
Defined in Web.Internal.FormUrlEncoded Associated Types
| |||||
| IsList Form # | _NOTE:_ | ||||
| Read Form # | |||||
| Show Form # | |||||
| Eq Form # | |||||
| FromForm Form # | |||||
| ToForm Form # | |||||
Defined in Web.Internal.FormUrlEncoded | |||||
| type Rep Form # | |||||
Defined in Web.Internal.FormUrlEncoded | |||||
| type Item Form # | |||||
Defined in Web.Internal.FormUrlEncoded | |||||
Encoding and decoding Forms
FormurlEncodeAsForm :: ToForm a => a -> ByteString #
This is a convenience function for encoding a datatype that has instance
of ToForm directly to a application/x-www-form-urlencoded
ByteString.
This is effectively .urlEncodeForm . toForm
_NOTE:_ this encoding is unstable and may result in different key order
(but not values). For a stable encoding see urlEncodeAsFormStable.
urlEncodeAsFormStable :: ToForm a => a -> ByteString #
This is a convenience function for encoding a datatype that has instance
of ToForm directly to a application/x-www-form-urlencoded
ByteString.
This is effectively .urlEncodeFormStable . toForm
>>>urlEncodeAsFormStable Person {name = "Dennis", age = 22}"age=22&name=Dennis"
urlDecodeAsForm :: FromForm a => ByteString -> Either Text a #
This is a convenience function for decoding a
application/x-www-form-urlencoded ByteString directly to a datatype
that has an instance of FromForm.
This is effectively .fromForm <=< urlDecodeForm
>>>urlDecodeAsForm "name=Dennis&age=22" :: Either Text PersonRight (Person {name = "Dennis", age = 22})
urlEncodeForm :: Form -> ByteString #
Encode a Form to an application/x-www-form-urlencoded ByteString.
_NOTE:_ this encoding is unstable and may result in different key order
(but not values). For a stable encoding see urlEncodeFormStable.
urlEncodeFormStable :: Form -> ByteString #
Encode a Form to an application/x-www-form-urlencoded ByteString.
For an unstable (but faster) encoding see urlEncodeForm.
Key-value pairs get encoded to key=value and separated by &:
>>>urlEncodeFormStable [("name", "Julian"), ("lastname", "Arni")]"lastname=Arni&name=Julian"
Keys with empty values get encoded to just key (without the = sign):
>>>urlEncodeFormStable [("is_test", "")]"is_test"
Empty keys are allowed too:
>>>urlEncodeFormStable [("", "foobar")]"=foobar"
However, if both key and value are empty, the key-value pair is ignored.
(This prevents from being a true isomorphism).urlDecodeForm . urlEncodeFormStable
>>>urlEncodeFormStable [("", "")]""
Everything is escaped with :escapeURIString isUnreserved
>>>urlEncodeFormStable [("fullname", "Andres Löh")]"fullname=Andres%20L%C3%B6h"
urlDecodeForm :: ByteString -> Either Text Form #
Decode an application/x-www-form-urlencoded ByteString to a Form.
Key-value pairs get decoded normally:
>>>urlDecodeForm "name=Greg&lastname=Weber"Right (fromList [("lastname","Weber"),("name","Greg")])
Keys with no values get decoded to pairs with empty values.
>>>urlDecodeForm "is_test"Right (fromList [("is_test","")])
Empty keys are allowed:
>>>urlDecodeForm "=foobar"Right (fromList [("","foobar")])
The empty string gets decoded into an empty Form:
>>>urlDecodeForm ""Right (fromList [])
Everything is un-escaped with unEscapeString:
>>>urlDecodeForm "fullname=Andres%20L%C3%B6h"Right (fromList [("fullname","Andres L\246h")])
Improperly formed strings result in an error:
>>>urlDecodeForm "this=has=too=many=equals"Left "not a valid pair: this=has=too=many=equals"
Generics
genericToForm :: (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form #
A Generic-based implementation of toForm.
This is used as a default implementation in ToForm.
Note that this only works for records (i.e. product data types with named fields):
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic)
In this implementation each field's value gets encoded using toQueryParam.
Two field types are exceptions:
- for values of type
an entry is added to theMaybeaFormonly when it isand the encoded value isJustx;toQueryParamxNothingvalues are omitted from theForm; - for values of type
[a](except[) an entry is added for every item in the list; if the list is empty no entries are added to theChar]Form;
Here's an example:
data Post = Post
{ title :: String
, subtitle :: Maybe String
, comments :: [String]
} deriving (Generic, Show)
instance ToForm Post
>>>urlEncodeAsFormStable Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] }"comments=Nice%20post%21&comments=%2B1&title=Test"
genericFromForm :: (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a #
A Generic-based implementation of fromForm.
This is used as a default implementation in FromForm.
Note that this only works for records (i.e. product data types with named fields):
data Person = Person
{ name :: String
, age :: Int
} deriving (Generic)
In this implementation each field's value gets decoded using parseQueryParam.
Two field types are exceptions:
- for values of type
an entry is parsed if present in theMaybeaFormand the is decoded withparseQueryParam; if no entry is present result isNothing; - for values of type
[a](except[) all entries are parsed to produce a list of parsed values;Char]
Here's an example:
data Post = Post
{ title :: String
, subtitle :: Maybe String
, comments :: [String]
} deriving (Generic, Show)
instance FromForm Post
>>>urlDecodeAsForm "comments=Nice%20post%21&comments=%2B1&title=Test" :: Either Text PostRight (Post {title = "Test", subtitle = Nothing, comments = ["Nice post!","+1"]})
Encoding options
data FormOptions #
Generic-based deriving options for ToForm and FromForm.
A common use case for non-default FormOptions
is to strip a prefix off of field labels:
data Project = Project
{ projectName :: String
, projectSize :: Int
} deriving (Generic, Show)
myOptions :: FormOptions
myOptions = FormOptions
{ fieldLabelModifier = map toLower . drop (length "project") }
instance ToForm Project where
toForm = genericToForm myOptions
instance FromForm Project where
fromForm = genericFromForm myOptions
>>>urlEncodeAsFormStable Project { projectName = "http-api-data", projectSize = 172 }"name=http-api-data&size=172">>>urlDecodeAsForm "name=http-api-data&size=172" :: Either Text ProjectRight (Project {projectName = "http-api-data", projectSize = 172})
Constructors
| FormOptions | |
Fields
| |
defaultFormOptions :: FormOptions #
Default encoding FormOptions.
FormOptions{fieldLabelModifier= id }
Helpers
toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] #
Parse a Form into a list of entries groupped by key.
_NOTE:_ this conversion is unstable and may result in different key order
(but not values). For a stable encoding see toEntriesByKeyStable.
toEntriesByKeyStable :: (Ord k, FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] #
Parse a Form into a list of entries groupped by key.
>>>toEntriesByKeyStable [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])]Right [("color",["red","white"]),("name",["Nick"])]
For an unstable (but faster) conversion see toEntriesByKey.
fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form #
Convert a list of entries groupped by key into a Form.
>>>fromEntriesByKey [("name",["Nick"]),("color",["red","blue"])]fromList [("color","red"),("color","blue"),("name","Nick")]
lookupAll :: Text -> Form -> [Text] #
Find all values corresponding to a given key in a Form.
>>>lookupAll "name" [][]>>>lookupAll "name" [("name", "Oleg")]["Oleg"]>>>lookupAll "name" [("name", "Oleg"), ("name", "David")]["Oleg","David"]
lookupMaybe :: Text -> Form -> Either Text (Maybe Text) #
Lookup an optional value for a key. Fail if there is more than one value.
>>>lookupMaybe "name" []Right Nothing>>>lookupMaybe "name" [("name", "Oleg")]Right (Just "Oleg")>>>lookupMaybe "name" [("name", "Oleg"), ("name", "David")]Left "Duplicate key \"name\""
lookupUnique :: Text -> Form -> Either Text Text #
Lookup a unique value for a key. Fail if there is zero or more than one value.
>>>lookupUnique "name" []Left "Could not find key \"name\"">>>lookupUnique "name" [("name", "Oleg")]Right "Oleg">>>lookupUnique "name" [("name", "Oleg"), ("name", "David")]Left "Duplicate key \"name\""
parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v] #
Lookup all values for a given key in a Form and parse them with parseQueryParams.
>>>parseAll "age" [] :: Either Text [Word8]Right []>>>parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]Left "could not parse: `seven' (input does not start with a digit)">>>parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]Left "out of bounds: `777' (should be between 0 and 255)">>>parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]Right [12,25]
parseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v) #
Lookup an optional value for a given key and parse it with parseQueryParam.
Fail if there is more than one value for the key.
>>>parseMaybe "age" [] :: Either Text (Maybe Word8)Right Nothing>>>parseMaybe "age" [("age", "12"), ("age", "25")] :: Either Text (Maybe Word8)Left "Duplicate key \"age\"">>>parseMaybe "age" [("age", "seven")] :: Either Text (Maybe Word8)Left "could not parse: `seven' (input does not start with a digit)">>>parseMaybe "age" [("age", "777")] :: Either Text (Maybe Word8)Left "out of bounds: `777' (should be between 0 and 255)">>>parseMaybe "age" [("age", "7")] :: Either Text (Maybe Word8)Right (Just 7)
parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v #
Lookup a unique value for a given key and parse it with parseQueryParam.
Fail if there is zero or more than one value for the key.
>>>parseUnique "age" [] :: Either Text Word8Left "Could not find key \"age\"">>>parseUnique "age" [("age", "12"), ("age", "25")] :: Either Text Word8Left "Duplicate key \"age\"">>>parseUnique "age" [("age", "seven")] :: Either Text Word8Left "could not parse: `seven' (input does not start with a digit)">>>parseUnique "age" [("age", "777")] :: Either Text Word8Left "out of bounds: `777' (should be between 0 and 255)">>>parseUnique "age" [("age", "7")] :: Either Text Word8Right 7
urlEncodeParams :: [(Text, Text)] -> ByteString #
Encode a list of key-value pairs to an application/x-www-form-urlencoded ByteString.
See also urlEncodeFormStable.
urlDecodeParams :: ByteString -> Either Text [(Text, Text)] #
Decode an application/x-www-form-urlencoded ByteString to a list of key-value pairs.
See also urlDecodeForm.