简体   繁体   English

如何使用scotty从sqlite和响应json获取数据?

[英]How to get data from sqlite and response json using scotty?

I'm trying to build a simple blog using Haskell and the Framework Scotty. 我正在尝试使用Haskell和Framework Scotty建立一个简单的博客。 Using a Model.hs I have: 使用Model.hs我有:

data Post = Post
    { id :: Int
    , tipo :: String
    , titulo :: String
    , conteudo :: String
    } deriving (Show, Generic)

I've already create a schema using sqlite and populate with some data, right now I'm trying to get this data using this method in my Storage.hs 我已经使用sqlite创建了一个架构并填充了一些数据,现在我正尝试在Storage.hs中使用此方法获取此数据。

selectPosts :: Sql.Connection -> IO [M.Post]
selectPosts conn =
    Sql.query_ conn "select * from post" :: IO [M.Post]

My intent is get the data format as json in my Main.hs: 我的意图是在Main.hs中将数据格式获取为json:

instance ToJSON M.Post
instance FromJSON M.Post

main :: IO ()
main = do
    putStrLn "Starting Server..."
    scotty 3000 $ do
        get "/" $ file "templates/index.html"
        get "/posts" $ do
            json posts where
            posts = withTestConnection $ \conn -> do
                S.selectPosts conn

But I'm getting an IO [Model Post] and I don't know how to render this as json, so its keeping getting this error: 但是我得到了一个IO [Model Post],但我不知道如何将其呈现为json,因此它不断出现此错误:

No instance for (ToJSON (IO [Post])) arising from a use of ‘json’

My project is in github to run just use stack build and after stack ghci. 我的项目在github中运行,仅在堆栈ghci之后运行即可。 In the building I'm already getting this error. 在建筑物中,我已经收到此错误。

In Haskell, all functions are pure---so something like selectPosts , which needs to go out and do IO to talk to a database, can't just do that and return the value from the database. 在Haskell中,所有函数都是纯函数,因此selectPosts东西需要走出去并做IO才能与数据库对话,不能仅仅这样做并从数据库返回值。 Instead, these kinds of functions return something of type IO a , which you can think of as basically a description of how to go out and do IO to get a value of type a . 相反,这些类型的函数返回的类型为IO a ,您可以将其基本上视为对如何执行和执行IO以获取类型a的值的描述。 These "IO actions" can be composed together, and one of them can be assigned to main ; 这些“ IO动作”可以组合在一起,并且可以将其中之一分配给main at runtime, the RTS will execute these IO actions. 在运行时,RTS将执行这些IO操作。

However, you aren't composing the IO a value that you get back from selectPosts to be part of the larger IO value that eventually becomes main ; 但是,您并不是将IO aselectPosts返回IO a值构成为最终成为main的较大IO值的一部分; you are trying to directly use it by feeding it into json . 您正在尝试通过将其输入json直接使用它。 This won't work, because there's no (good/easy) way to convert a description of how to do IO into a JSON string. 这是行不通的,因为没有(好的/简单的)方法可以将有关如何将IO的描述转换为JSON字符串。

The way that Haskell deals with composing these values is through an abstraction known as a "monad", which is useful in a lot of other cases as well. Haskell处理这些值的方式是通过称为“ monad”的抽象进行,在许多其他情况下也很有用。 do notation can be used to write this monadic sequencing in a very natural style. do记号可以用在一个非常自然的风格来写这个单子测序。 You can't just write posts <- withTestConnection S.selectPosts here because Scotty's get function takes a value of the monadic ActionM type, not IO . 您不能只在此处用posts <- withTestConnection S.selectPosts编写posts <- withTestConnection S.selectPosts ,因为Scotty的get函数采用的是ActionM类型的值,而不是IO However, it turns out that ActionM is basically a bunch of other useful stuff layered on top of IO , so it should be possible to "lift" the IO action from selectPosts into Scotty's ActionM monad: 但是,事实证明ActionM基本上是一堆在IO之上的其他有用的东西,因此应该有可能将IO操作从selectPosts提升到Scotty的ActionM monad中:

get "/posts" $ do
  posts <- liftIO $ withTestConnection S.selectPosts
  json posts

Side note: You may have noticed that I wrote withTestConnection S.selectPosts instead of withTestConnection $ \\conn -> do S.selectPosts conn . 旁注:您可能已经注意到我写了withTestConnection S.selectPosts而不是withTestConnection $ \\conn -> do S.selectPosts conn Generally, if you have a do block with only one expression in it (not of the form x <- act ), this is the same as the single expression outside of a do block: \\conn -> S.selectPosts conn . 通常,如果您有一个do块,其中只包含一个表达式(不是x <- act \\conn -> S.selectPosts conn形式),则与do块之外的单个表达式相同: \\conn -> S.selectPosts conn Furthermore, Haskell tends to encourage partial application: you have S.selectPosts , which is a function Sql.Connection -> IO [M.Post] . 此外,Haskell倾向于鼓励部分应用:您拥有S.selectPosts ,这是一个Sql.Connection -> IO [M.Post]函数。 \\conn -> S.selectPosts conn is another function, of the same type, which passes the connection into selectPosts and then returns the same result as selectPosts ---this function is indistinguishable from selectPosts itself! \\conn -> S.selectPosts conn是另一种相同类型的函数,它将连接传递到selectPosts ,然后返回与selectPosts相同的结果---此函数与selectPosts本身是无法区分的! So if this is all that you need inside your withTestConnection , you should be able to simplify the whole lambda and do block to just S.selectPosts . 因此,如果这是所有你需要你的内withTestConnection ,你应该能够简化整个的Lambda和do阻止,只是S.selectPosts

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM