我在设置简单的概念服务 API 证明时遇到问题。这是我的用户数据类型和我的 API 类型:
data User = User { id :: Int, first_name :: String, last_name :: String } deriving (Eq, Show, Generic)
instance FromRow User
instance ToRow User
$(deriveJSON defaultOptions ''User)
type API = "users" :> ReqBody '[JSON] User :> Post '[JSON] User
此处理程序方法使用 postgresql-simple,如下所示:
create u = liftIO $ head <$> returning connection "insert into users (first_name, last_name) values (?,?) returning id" [(first_name u, last_name u)]
样板代码,例如连接到数据库和路由方法已被省略。
问题是,如果我发出 POST 请求,我想做的是创建一个新用户,因此我将提供 JSON:
{ "first_name": "jeff", "last_name": "lebowski" }
但是我的程序在运行时失败了
Error in $: When parsing the record User of type Lib.User the key id was not present.
这是有道理的,因为 API 指定了一个具有 id 字段的用户。但我不想在请求中传递一个虚假的 id(因为它们是由 postgres 按顺序分配的),因为这很糟糕。我也无法将 id 字段移出 User 数据类型,因为在向不同端点发出 GET 请求时 postgres-simple 由于模型数据库不匹配而失败(这很明显:通过 id 获取。未包括在上面)。
我在这里做什么?编写自定义 FromJson 实例?我已经尝试将 Data.Aeson.TH 选项标志 omitNothingFields 设置为 True 并将 id 字段设置为 Maybe Int,但这也不起作用。
任何意见,将不胜感激。
最佳答案
首先你要明白一个User和这个User对应的表中的一行是两个不同的东西。
一行有一个 id,用户没有。例如,您可以想象在不处理 id 或保存与否的情况下比较两个用户。
一旦被说服,您将不得不向类型系统解释这一点,或者您将不得不处理 Maybe 字段,我认为这不是这里的解决方案。
有人讲了Template Haskell,我觉得这里说的有点过头了,需要先解决问题。
您可以做的是使用数据类型来表示数据库中保存的行。我们称之为实体。
newtype PrimaryKey = PrimaryKey Int
data Entity b = Entity PrimaryKey b
然后在数据库中保存用户行的函数可能需要
user
作为参数并返回 PrimaryKey
(当然,在你的数据库 monad 中)。从数据库读取的其他函数将使用
Entity User
返回一些内容。您的字段声明不会重复,因为您将 User 类型重用为参数。
您必须相应地调整 FromRow/ToRow 和 FromJSON/ToJSON。
关于haskell - 带有仆人的数据库支持的 REST API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38818478/