Search code examples
haskellcurlyesod

How can i display curl results on an html page, using yesod?


I want to post the results of a curl command within an html page, using the yesod framework in haskell. This is the code i have so far:

{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses,
         TemplateHaskell, OverloadedStrings #-}
import Yesod
import Network.Curl
import Text.Blaze hiding (toMarkup)

data HelloWorld = HelloWorld

mkYesod "HelloWorld" [parseRoutes|
/ HomeR GET
|]

url = "http://www.google.com/"
opts = [CurlFollowLocation True]

res=withCurlDo $ do
            curlGet url opts
            return ()

instance ToMarkup (IO a) where
toMarkup a = a

instance Yesod HelloWorld

getHomeR :: Handler RepHtml
getHomeR = defaultLayout [whamlet|#{toMarkup res}|]

main :: IO ()
main = warpDebug 3000 HelloWorld

This code launches the server with the warning

Warning: No explicit method nor default method for `Text.Blaze.toMarkup'
In the instance declaration for `ToMarkup (IO a)'

and on pointing the web browser to

http://localhost:3000

it gives "Internal Server Error" along the above warning message as an HTML page.

I'm fairly new to Haskell and Yesod... could someone help?


Solution

  • Your indentation is wrong for toMarkup (it should be indented). However the types are still wrong. toMarkup should return a Markup instance and curlGet dumps the output to stdout, whereas you want to capture it and re-render it.

    Try something like this:

    {-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings #-}
    import Yesod
    import Network.Curl
    
    data HelloWorld = HelloWorld
    
    mkYesod "HelloWorld" [parseRoutes|
          / HomeR GET
        |]
    
    url = "http://www.google.com/"
    opts = [CurlFollowLocation True]
    
    instance Yesod HelloWorld
    
    getHomeR = do
        (code, res) <- liftIO $ curlGetString url opts        
        -- This doesn't work since Yesod HTML-escapes the content in the template
        -- defaultLayout [whamlet|#{res}|]
        return $ RepHtml $ toContent res
    
    main :: IO ()
    main = warpDebug 3000 HelloWorld
    

    As an alternative to curl, you could also use the http-conduit package. Import Net.HTTP.Conduit and you could write getHomeR as:

    getHomeR = fmap (RepHtml . toContent) . liftIO $ simpleHttp "http://www.google.com"