EDIT2:我已经开始通过非常明确地了解数据类型并且每个语义数据单元具有多种数据类型来解决这个问题(几周后)。例如,如果数据库包含X
,则我的代码有一个XAction
用于表示我要对X
执行的操作,而XResponse
用于表示X
转到http客户端。然后我需要构建支持代码来在实例之间穿梭。不理想,但是,我喜欢它的明确,并且希望当我的模型结晶时,它不应该真的需要很多保持,并且应该非常可靠。
我不确定解决这个问题的正确抽象级别是什么(即记录?还是Yesod?)所以我只是简单地说明了这个问题。
我想将请求主体解码为类型
data Comment = Comment {userid :: ..., comment :: ...}
但实际上我并不希望请求正文包含userid
,服务器将根据其Auth Headers(或者我想将数据设置为默认填充字段的任何地方)提供。{ / p>
所以他们实际上传给我的是:
data SimpleComment = SimpleComment {comment :: ...} deriving (Generic, FromJSON)
然后我把它变成Comment
。但同时保持几乎相同的类型是麻烦,而不是干。
如何解决这个问题?
我有一个记录类型:
data Comment = Comment {userid :: ..., comment :: ...}
我有一条POST路线:
postCommentR :: Handler Value
postCommentR = do
c <- requireJsonBody :: (Handler Comment)
insertedComment <- runDB ...
returnJson insertedComment
请注意,路由要求用户提供userid
(Comment
类型,这至少是冗余的,因为它们的id与其auth标头相关联。最坏的情况是,这意味着我需要检查用户是否正在添加自己的ID,或丢弃他们提供的ID,在这种情况下,为什么他们在第一种情况下提供它。
所以,我想要一个Comment
减去userid
的记录类型,但我不知道如何智能地做到这一点。
所以我创建了一个带有派生FromJSON
的自定义类型(对于请求体),它几乎完全是Comment
类型的冗余。
data SimpleComment = SimpleComment {comment :: ...} deriving (Generic, FromJSON)
然后我的新路由需要根据此解码请求正文,然后将SimpleComment
与userid
字段合并以使其成为Comment
:
postComment2R :: Handler Value
postComment2R = do
c <- requireJsonBody :: (Handler SimpleComment)
(uid, _) requireAuthPair
insertedComment <- runDB $ insertEntity (Comment { commentUserid = uid
, commentComment = comment c})
returnJson ...
谈论样板。我的用例比这个简单的Comment
类型更复杂。
如果它有所影响,您可以告诉我,我正在使用 Yesod Scaffolding 。
答案 0 :(得分:3)
我通常做的是为了获得一个类型减去一个字段只是为了有一个函数,它接受该字段并返回类型。在您的情况下,您只需要为UserId -> Comment
声明一个JSON实例。好吧,这似乎并不自然,你必须手动去,但实际上效果很好,特别是因为评论中只有一个UserId类型的字段。
答案 1 :(得分:2)
我喜欢的解决方案是使用包装来处理来自/转到DB的东西:
data Authenticated a = Authenticated
{ uid :: Uid
, thing :: a
} deriving (Show)
然后,您可以让Comment
成为SimpleComment
,并在知道用户ID后将其变为Authenticated Comment
。
答案 2 :(得分:0)
我也在寻找一种解决这个问题的好方法。 : - )
我通常在代码中执行的操作是直接在Aeson的类型Value
上操作。这是我当前项目中的一些示例代码:
import qualified Data.HashMap.Strict as HM
removeKey :: Text -> Value -> Value
removeKey key (Object xs) = Object $ HM.delete key xs
removeKey _ ys = ys
我直接操作值Object
并删除javascript对象中的特定键。
在Yesod处理程序代码中,我执行此处理:
myHandler :: Handler RepJson
myHandler = do
userId <- insert $ User "sibi" 23
guser <- getJuser user
let guser' = removeKey "someId" $ toJSON guser
return $ repJson $ object [ "details" .= guser' ]
在某些情况下,我实际上想要为传出的JSON对象添加一些特定的键。对于那些,我定义了特定的辅助函数,它们在类型Value
上运行。虽然这并不完美,但它一直在帮助我避免大量的样板代码。