Using Servant with Yesod shakespeare (Hamlet, Juli

2019-06-07 03:17发布

问题:

How i can use shakespeare (from yesod) for servant webservices APIs?

I try:

type TestAPI 
    = "tests" :> Get '[JSON] [Test]
    :<|> "Test.html" :> Get '[HTML] Html

serverTestAPI :: ServerT TestAPI AppM
serverTestAPI = tests 
           :<|> test
           :<|> testHtml

tests :: AppM [Test]
tests = do return [ Test 1 "Test 1"
                  , Test 2 "Test 2"
                  ]

testHtml = [hamlet|
                $doctype 5
                 .........
                 |]

But I get error!

回答1:

As @Carsten points out, in this case what you want is shamlet. The key thing is implementing proper instances of the ToMarkup typeclass. I recommend you to read this excellent introduction on Shakespeare templates. A working example:

data Person = Person
  { firstName :: String
  , lastName  :: String
  } deriving Generic 

instance ToJSON Person

type PersonAPI = "persons" :> Get '[JSON, HTML] [Person]

people :: [Person]
people =
  [ Person "Isaac"  "Newton"
  , Person "Albert" "Einstein"
  ]

instance ToMarkup Person where
  toMarkup person = showPerson person

  -- this isn't properly implemented
  preEscapedToMarkup p = showPerson p

-- HTML serialization of a list of persons
instance ToMarkup [Person] where
  toMarkup persons = showPersons persons

  preEscapedToMarkup p = showPersons p

showPerson :: Person -> Html
showPerson p = [shamlet|
<body>
    <p>This is my page.
    <h1>#{firstName p}
|]

showPersons :: [Person] -> Html
showPersons p = [shamlet|
<body>
    <p>This is my page.
     $forall person <- p
      <h1>#{firstName person}
|]


回答2:

Here is a small complete example that works for me:

{-# LANGUAGE DataKinds, PolyKinds, TypeOperators, DeriveGeneric #-}
{-# LANGUAGE QuasiQuotes #-}
module Main where

import Data.Aeson
import Data.Proxy
import GHC.Generics
import Network.Wai.Handler.Warp
import Servant.API
import Servant.HTML.Blaze
import Servant.Server
import Text.Blaze.Html
import Text.Hamlet

data Test = Test Int String
  deriving (Generic)

instance ToJSON Test

type TestAPI 
    =    "tests" :> Get '[JSON] [Test]
    :<|> "Test.html" :> Get '[HTML] Html

main :: IO ()
main = run 8080 (serve (Proxy :: Proxy TestAPI) serverTestAPI)

serverTestAPI :: Server TestAPI
serverTestAPI = tests :<|> testHtml

tests = return [ Test 1 "Test 1"
               , Test 2 "Test 2"
               ]

testHtml = return [shamlet|
  $doctype 5
  <html>
     <head>
       <title>This is a title
     <body>
       <p>This is text
  |]


回答3:

Thank you all for your help!
Implemented as follows:

<code>type TestAPI 
    = "tests" :> Get '[JSON] [Test]
    :<|> "test" :> Get '[JSON] Test
    :<|> "TestHTML.html" :> Get '[HTML] Page_TestHTML 

serverTestAPI :: ServerT TestAPI AppM
serverTestAPI = tests 
           :<|> test
           :<|> testHtml

data Page_TestHTML = Page_TestHTML

instance ToMarkup Page_TestHTML where
    toMarkup Page_TestHTML = builderHtml  

testHtml = return Page_TestHTML

builderHtml = [shamlet|
                $doctype 5
                <html>
                    <head>
                        <title>Greeting2
                <body>
                    <h2> Hello world HTML Qqqqq |]</code>