Help us understand the problem. What is going on with this article?

【Servant】(6) HTML

【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  ###返り値

今回は以上です。

sand
Haskell、Elm、Elixir、Phoenixなどが好きな言語です
http://www.mypress.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした