Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for validating JSON requests with Yesod

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:

Request

{
    "birthDate": "2017.07.14",
    "count": "three",
    "kind": "baz",
    "entity": {
        "id": -1
    }
}

Response

{
    "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"
        }
    }
}
like image 390
Andrey Kuznetsov Avatar asked May 25 '14 09:05

Andrey Kuznetsov


2 Answers

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.

like image 85
Michael Snoyman Avatar answered Sep 30 '22 16:09

Michael Snoyman


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.

like image 25
Andrey Kuznetsov Avatar answered Sep 30 '22 18:09

Andrey Kuznetsov