【Servant】(6) HTML

[【Servant】(1) Wai - Qiita](https://qiita.com/sand/items/16a6ef431a8376e7cbaa)
[【Servant】(2) Servantチュートリアル - Qiita](https://qiita.com/sand/items/fea7c294dd7835692ff7)
[【Servant】(3) エンドポイントを増やす - Qiita](https://qiita.com/sand/items/3af359b1830453d1d0ed)
[【Servant】(4) URLパラメータをハンドラの引数とする - Qiita](https://qiita.com/sand/items/d1c0bfb84487a1dfa9bb)
[【Servant】(5) JSON - Qiita](https://qiita.com/sand/items/fd2ea8acdc56998d5c5e)
[【Servant】(6) HTML - Qiita](https://qiita.com/sand/items/0e9f3c80b5103b966be4)
[【Servant】(7) Post Data - Qiita](https://qiita.com/sand/items/d21aed7c24521e20f1cb)
[【Servant】(8) Another Monad - Qiita](https://qiita.com/sand/items/9c8fa50815b00daead24)
[【Servant】(9) Handlerモナド - Qiita](https://qiita.com/sand/items/ae649f14f1b598d636c3)
[【Servant】(10) SQLite - Qiita](https://qiita.com/sand/items/091b0ea981152ad7d744)
[【Servant】(11) Servant-Client - Qiita](https://qiita.com/sand/items/a0e7758da7274a119849)
[【Servant】(12) Basic 認証 - Qiita](https://qiita.com/sand/items/dba678cef365fbd23ae3)



今回からは、以下のexampleを取り上げていきます。
[Example Projects - haskell-servant/servant@github](https://github.com/haskell-servant/servant/blob/master/doc/examples.md)

今回の記事は、前回の記事「[【Servant】(5) JSON](https://qiita.com/sand/items/fd2ea8acdc56998d5c5e) 」の復習・応用になります。


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

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


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



```haskell:
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型の値は定義していませんでしたね。


```haskell:pack
pack :: [Char] -> ByteString
```

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

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

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

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

handlerAge :: Handler Int
handlerAge = return 30

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

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

```haskell: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
```


メイン


```haskell:Main.hs
module Main where

import HtmlContent

main :: IO ()
main = runServant
```

使用するパッケージ

```yaml: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  ###返り値
```

今回は以上です。