I am writing JSON RESTFul service with Yesod and need to implement requests validation. I cannot use Yesod.Form for it because of the service accepts JSON. I like the way scalaz validation uses but I didn't find any approach like this in Haskell.
Is there a best practice to implement validation which would allow to answer with structured errors messages, something like this:
{
"birthDate": "2017.07.14",
"count": "three",
"kind": "baz",
"entity": {
"id": -1
}
}
{
"errors": {
"birthDate": "Date should be less than 2014.05.25", // current date
"count": "should be a number",
"kind": "must be one of [foo, bar]",
"entity": {
"id": "Entity with id -1 not found"
}
}
}
I'd recommend using aeson's native parsing abilities for this, which will simultaneously parse the incoming JSON and convert it into a Haskell data structure.
There is a digestive-functors-aeson library that utilizes Text.Digestive.Form
and allows to write expressive validation rules. Here is a gist from its tests:
pokeForm :: Monad m => Form Text m Pokemon
pokeForm = Pokemon <$> "name" .: nonEmptyText
<*> "number" .: parseInteger
where
nonEmptyText = check "Name cannot be empty" (not . T.null) $
text Nothing
testPokedexFailHead =
let (v, r) = runIdentity $ digestJSON pokedexForm json
in testGroup "Submit pokedex with a single invalid item"
[ testCase "Failed validation" $ r @?= Nothing
, testCase "jsonErrors shows correct errors" $ jsonErrors v @?= errors
]
where
(Just json) = decode "{\"pokemon\":[{\"name\":\"\"}]}"
(Just errors) = decode "{\"pokemon\":[{\"name\":\"Name cannot be empty\"}]}"
As you can see the library can produces pretty verbose errors which are labeled with field names which failed validation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With