使用Yesod验证JSON请求的最佳实践

And*_*sov 8 rest json haskell yesod

我正在使用Yesod编写JSON RESTFul服务,需要实现请求验证.因为服务接受JSON,我不能使用Yesod.Form.我喜欢scalaz验证使用的方式,但我没有在Haskell中找到这样的方法.

是否有最佳实践来实现验证,允许回答结构化错误消息,如下所示:

请求

{
    "birthDate": "2017.07.14",
    "count": "three",
    "kind": "baz",
    "entity": {
        "id": -1
    }
}
Run Code Online (Sandbox Code Playgroud)

响应

{
    "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"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*man 7

我建议使用aeson的本机解析功能,它将同时解析传入的JSON并将其转换为Haskell数据结构.


And*_*sov 5

有一个消化功能类库,该库利用Text.Digestive.Form并允许编写表达性验证规则。这是其测试的要点:

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\"}]}"
Run Code Online (Sandbox Code Playgroud)

如您所见,该库会产生非常冗长的错误,并标有未通过验证的字段名称。