1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Servant】(6) HTML

Last updated at Posted at 2020-02-23

【Servant】(1) Wai - Qiita
【Servant】(2) Servantチュートリアル - Qiita
【Servant】(3) エンドポイントを増やす - Qiita
【Servant】(4) URLパラメータをハンドラの引数とする - Qiita
【Servant】(5) JSON - Qiita
【Servant】(6) HTML - Qiita
【Servant】(7) Post Data - Qiita
【Servant】(8) Another Monad - Qiita
【Servant】(9) Handlerモナド - Qiita
【Servant】(10) SQLite - Qiita
【Servant】(11) Servant-Client - Qiita
【Servant】(12) Basic 認証 - Qiita

今回からは、以下のexampleを取り上げていきます。
Example Projects - haskell-servant/servant@github

今回の記事は、前回の記事「【Servant】(5) JSON 」の復習・応用になります。

1. HTMLを返すAPIを実装する

ここではHTMLを返すAPIを定義します。

まずは前回の記事「【Servant】(5) JSON」を踏まえて、content type "text/html" に対応する data型HTMLを定義し、AcceptクラスとMimeRenderクラスのインスタンスとして実装していきます。

data HTML -- Here is our HTML type that we will use in the type of the endpoint.
          -- We don't need a constructor here since we ll ever have to deal with a value of this type.

instance Accept HTML where         -- This instance is what makes the endpoints with this content type
   contentType _ = "text/html"     -- return content with a content type header with "text/html" in it.

instance MimeRender HTML String  where  -- This instance where we define how a value of type string is
   mimeRender _ val = C.pack val        -- is encoded as an html value. Note that we are not converting
                                        -- the string to an value of type HTML, but just to a Bytestring that
                                        -- represents the HTML encoding. As I said earlier, we won't ever
                                        -- have to deal with a value of type HTML

instance MimeRender HTML Int  where        -- Same as before. This instance defines how an Int will be converted
   mimeRender _ val = C.pack $ show $ val  -- to a bytestring for endpoints with HTML content type.

まず 「data HTML」を宣言します。型名だけを定義し、値コンストラクタについては何も触れません。HTML型の値については今後触れることはないからです。

次に「instance Accept HTML」を定義し、contentType が "text/html" を返すようにします。

最後に「instance MimeRender HTML String」の定義です。StringをLazy ByteString へ変換(エンコーディング)します。このLazy ByteStringはHTML encodingを表現していますが、HTML型の値へ変換しているわけではありません。そもそも「data HTML」宣言においてHTML型の値は定義していませんでしたね。

pack
pack :: [Char] -> ByteString

APIの定義です。 Get '[HTML] String と Get '[HTML] Int を利用します。これは上で「instance MimeRender HTML String」と「instance MimeRender HTML Int」を実装に対応します。

API
type ServantType =  "name" :> Get '[HTML] String
               :<|> "age" :> Get '[HTML] Int

APIに対するHandllerを定義します。

Handller
handlerName :: Handler String
handlerName = return "sras"

handlerAge :: Handler Int
handlerAge = return 30

server :: Server ServantType
server = handlerName :<|> handlerAge

APIとHandllerでApplicationを実装します。

Application
app :: Application
app = serve (Proxy :: Proxy ServantType) server

2. 全ソース

HtmlContentソース本体

HtmlContent.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module HtmlContent
    ( runServant
    ) where

import Servant ( QueryParam
               , PlainText
               , Get
               , Proxy(..)
               , type (:>)      -- Syntax for importing type operator
               , type (:<|>)
               , (:<|>)(..)
               , Accept(..)
               , MimeRender(..)
               )
import Servant.Server (Handler, Server, Application, serve)
import Network.Wai.Handler.Warp (run)
import Control.Monad.IO.Class (liftIO)
import Data.ByteString.Lazy.Char8 as C

-- In this example, we add an Html type and make endpoints
-- with this content type return a content type header with
-- "text/html" in it.

data HTML -- Here is our HTML type that we will use in the type of the endpoint.
          -- We don't need a constructor here since we ll ever have to deal with a value of this type.

instance Accept HTML where         -- This instance is what makes the endpoints with this content type
   contentType _ = "text/html"     -- return content with a content type header with "text/html" in it.

instance MimeRender HTML String  where  -- This instance where we define how a value of type string is
   mimeRender _ val = C.pack val        -- is encoded as an html value. Note that we are not converting
                                        -- the string to an value of type HTML, but just to a Bytestring that
                                        -- represents the HTML encoding. As I said earlier, we won't ever
                                        -- have to deal with a value of type HTML

instance MimeRender HTML Int  where        -- Same as before. This instance defines how an Int will be converted
   mimeRender _ val = C.pack $ show $ val  -- to a bytestring for endpoints with HTML content type.

type ServantType =  "name" :> Get '[HTML] String
               :<|> "age" :> Get '[HTML] Int

handlerName :: Handler String
handlerName = return "<h1>sras</h1>"

handlerAge :: Handler Int
handlerAge = return 30

server :: Server ServantType
server = handlerName :<|> handlerAge

app :: Application
app = serve (Proxy :: Proxy ServantType) server


runServant :: IO ()
runServant = run 4000 app


-- Now let us see how the app behaves
--
--
-- curl -v 127.0.0.1:4000/age
-- *   Trying 127.0.0.1...
-- * Connected to 127.0.0.1 (127.0.0.1) port 4000 (#0)
-- > GET /age HTTP/1.1
-- > Host: 127.0.0.1:4000
-- > User-Agent: curl/7.47.0
-- > Accept: */*
-- >
-- < HTTP/1.1 200 OK
-- < Transfer-Encoding: chunked
-- < Date: Sun, 12 Nov 2017 13:16:50 GMT
-- < Server: Warp/3.2.13
-- < Content-Type: text/html
-- <
-- * Connection #0 to host 127.0.0.1 left intact
-- 30

メイン

Main.hs
module Main where

import HtmlContent

main :: IO ()
main = runServant

使用するパッケージ

package.yaml
dependencies:
- base >= 4.7 && < 5
- servant
- servant-server
- aeson
- time
- wai
- warp
- http-media
- bytestring

3. 実行結果

nameを叩く

$ curl -v  127.0.0.1:4000/name
*   Trying 127.0.0.1:4000...
* TCP_NODELAY set
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to 127.0.0.1 (127.0.0.1) port 4000 (#0)
> GET /name HTTP/1.1
> Host: 127.0.0.1:4000
> User-Agent: curl/7.67.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Date: Sun, 23 Feb 2020 03:32:55 GMT
< Server: Warp/3.3.5
< Content-Type: text/html ###コンテントタイプ
<
{ [26 bytes data]
100    13    0    13    0     0  13000      0 --:--:-- --:--:-- --:--:-- 13000
<h1>sras</h1> ###返り値

ageを叩く

$ curl -v  127.0.0.1:4000/age
*   Trying 127.0.0.1:4000...
* TCP_NODELAY set
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to 127.0.0.1 (127.0.0.1) port 4000 (#0)
> GET /age HTTP/1.1
> Host: 127.0.0.1:4000
> User-Agent: curl/7.67.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Date: Sun, 23 Feb 2020 03:33:14 GMT
< Server: Warp/3.3.5
< Content-Type: text/html  ###コンテントタイプ
<
{ [15 bytes data]
100     2    0     2    0     0   2000      0 --:--:-- --:--:-- --:--:--  2000
30  ###返り値

今回は以上です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?