| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Web.FormUrlEncoded
Contents
Description
Convert Haskell values to and from application/xxx-form-urlencoded format.
- class ToForm a where
- class FromForm a where
- class ToFormKey k where
- class FromFormKey k where
- newtype Form = Form {}
- urlEncodeAsForm :: ToForm a => a -> ByteString
- urlDecodeAsForm :: FromForm a => ByteString -> Either Text a
- urlEncodeForm :: Form -> ByteString
- urlDecodeForm :: ByteString -> Either Text Form
- genericToForm :: forall a. (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form
- genericFromForm :: forall a. (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a
- data FormOptions = FormOptions {
- fieldLabelModifier :: String -> String
- defaultFormOptions :: FormOptions
- toEntriesByKey :: (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
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.
class FromForm a where Source #
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.
Methods
fromForm :: Form -> Either Text a Source #
Parse Form into a value.
fromForm :: (Generic a, GFromForm a (Rep a)) => Form -> Either Text a Source #
Parse Form into a value.
Instances
| FromForm Form Source # | |
| (FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] Source # | |
| FromHttpApiData v => FromForm (IntMap [v]) Source # | |
| (Ord k, FromFormKey k, FromHttpApiData v) => FromForm (Map k [v]) Source # | |
| (Eq k, Hashable k, FromFormKey k, FromHttpApiData v) => FromForm (HashMap k [v]) Source # | |
Keys for Form entries
class ToFormKey k where Source #
Minimal complete definition
Instances
class FromFormKey k where Source #
Minimal complete definition
Instances
Form type
The contents of a form, not yet URL-encoded.
Form can be URL-encoded with urlEncodeForm and URL-decoded with urlDecodeForm.
Encoding and decoding Forms
FormurlEncodeAsForm :: ToForm a => a -> ByteString Source #
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
>>>urlEncodeAsForm Person {name = "Dennis", age = 22}"age=22&name=Dennis"
urlDecodeAsForm :: FromForm a => ByteString -> Either Text a Source #
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 Source #
Encode a Form to an application/x-www-form-urlencoded ByteString.
Key-value pairs get encoded to key=value and separated by &:
>>>urlEncodeForm [("name", "Julian"), ("lastname", "Arni")]"lastname=Arni&name=Julian"
Keys with empty values get encoded to just key (without the = sign):
>>>urlEncodeForm [("is_test", "")]"is_test"
Empty keys are allowed too:
>>>urlEncodeForm [("", "foobar")]"=foobar"
However, if not key and value are empty, the key-value pair is ignored. (This prevents from being a true isomorphism).urlDecodeForm . urlEncodeForm
>>>urlEncodeForm [("", "")]""
Everything is escaped with :escapeURIString isUnreserved
>>>urlEncodeForm [("fullname", "Andres Löh")]"fullname=Andres%20L%C3%B6h"
urlDecodeForm :: ByteString -> Either Text Form Source #
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 :: forall a. (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form Source #
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 >>>urlEncodeAsForm Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] }"comments=Nice%20post%21&comments=%2B1&title=Test"
genericFromForm :: forall a. (Generic a, GFromForm a (Rep a)) => FormOptions -> Form -> Either Text a Source #
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 Source #
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 >>>urlEncodeAsForm Project { projectName = "http-api-data", projectSize = 172 }"size=172&name=http-api-data">>>urlDecodeAsForm "name=http-api-data&size=172" :: Either Text ProjectRight (Project {projectName = "http-api-data", projectSize = 172})
Constructors
| FormOptions | |
Fields
| |
defaultFormOptions :: FormOptions Source #
Default encoding FormOptions.
FormOptions{fieldLabelModifier= id }
Helpers
toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] Source #
Parse a Form into a list of entries groupped by key.
>>>toEntriesByKey [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])]Right [("color",["red","white"]),("name",["Nick"])]
fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form Source #
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] Source #
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) Source #
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 Source #
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] Source #
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) Source #
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 Source #
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