Forms¶
When working with form data, we often want to serialize and deserialize
forms as custom data types, instead of working with the key-value pairs
directly. The ToForm
and FromForm
type classes abstracts
serialization and deserialization to form data, respectively.
We first declare our data types, and some instance which we will need later.
data MealType = Vegan | Vegetarian | Omnivore | Carnivore
derive instance genericMealType :: Generic MealType _
instance eqMealType :: Eq MealType where eq = genericEq
instance showMealType :: Show MealType where show = genericShow
newtype Order = Order { beers :: Int, meal :: MealType }
In this example we will only deserialize forms, and thus we only need
the FromForm
instance.
instance fromFormOrder :: FromForm Order where
fromForm form = do
beers <- required "beers" form >>= parseBeers
meal <- required "meal" form >>= parseMealType
pure (Order { beers: beers, meal: meal })
where
parseBeers s =
maybe
(throwError ("Invalid number: " <> s))
pure
(Int.fromString s)
parseMealType =
case _ of
"Vegan" -> pure Vegan
"Vegetarian" -> pure Vegetarian
"Omnivore" -> pure Omnivore
"Carnivore" -> pure Carnivore
s -> throwError ("Invalid meal type: " <> s)
Now we are ready to write our handler. We use parseFromForm
to get a
value of type Either String Order
, where the String
explains
parsing errors. By pattern matching using record field puns, we extract
the beers
and meal
values, and respond based on those values.
onPost =
parseFromForm :>>=
case _ of
Left err ->
writeStatus statusBadRequest
:*> closeHeaders
:*> respond (err <> "\n")
Right (Order { beers, meal })
| meal == Omnivore || meal == Carnivore ->
writeStatus statusBadRequest
:*> closeHeaders
:*> respond "Sorry, we do not serve meat here.\n"
| otherwise ->
writeStatus statusBadRequest
:*> closeHeaders
:*> respond ("One " <> show meal <> " meal and "
<> show beers <> " beers coming up!\n")
Let’s try this server out at the command line.
$ curl -X POST -d 'beers=6' http://localhost:3000
Missing field: meal
$ curl -X POST -d 'meal=Vegan&beers=foo' http://localhost:3000
Invalid number: foo
$ curl -X POST -d 'meal=Omnivore&beers=6' http://localhost:3000
Sorry, we do not serve meat here.
$ curl -X POST -d 'meal=Vegetarian&beers=6' http://localhost:3000
One Vegetarian meal and 6 beers coming up!