HaskellでCGI
最近「Haskellで~」ってタイトルばっかですね。
今回のも「CGIを書く」とかでいいのかもしれない。っていうほどHaskellのことばかり書いてるわけでもないのでそれはやり過ぎかもしれない。
たぶんそのうちRubyの記事も書きます。
Hello World
で、HaskellでCGIを書きます。
index.cgi.hs
main :: IO () main = do putStrLn "Content-type: text/plain" putStrLn "" putStrLn "It will be fine, tomorrow!"
おめでとうございます。これでHaskellでCGIが書けました!
でもこの程度ならシェルスクリプトでいいじゃんって話です。
CGIライブラリを使う
CGIライブラリを使って、入力を受け取ったりできるようにしてみましょう。
% cabal install cgi
これのHello World的なものはマニュアルに書いてあります。
Network.CGI
import Network.CGI cgiMain :: CGI CGIResult cgiMain = output "Hello World!" main :: IO () main = runCGI (handleErrors cgiMain)
outputは自動的にヘッダを付加して出力してくれます。
ただし、ここで自動的に付加されるヘッダには何故かcharsetが指定されているので、日本語を表示しようとするとmetaタグを書こうが何をしようがブラウザによっては文字化けが起きる。
なので結局ヘッダは自前で設定する羽目になる。こういう感じ。
import Network.CGI cgiMain :: CGI CGIResult cgiMain = do setHeader "Content-type" "text/plain" output "Hello World!" main :: IO () main = runCGI (handleErrors cgiMain)
setHeaderは1回以上呼びだすとデフォルトのヘッダが上書きされて消える。2回以上呼び出すと呼び出した数だけヘッダが付加される。まあ見たまんま感じたまんまですが、実装したくねぇなぁって感じです。
できたファイルはそのまま実行もできるので、パラメータが無い時の表示チェックとかに使えます。まあこの辺は普通のCGIスクリプトと同じです。
% ghc index.cgi.hs % ./index.cgi Content-type: text/plain Hello World!
putStrLnとか書くのめんどくさい
ここで直接は関係ないんですが、HTMLを出力するの面倒ですよね。
Haskellにもテンプレートエンジンとかあるけど、そこまでやんなくても、とりあえずヒアドキュメント的なの無いんですか?
まあ、無いので作ります。
Str.hs
module Str(str) where import Language.Haskell.TH import Language.Haskell.TH.Quote str :: QuasiQuoter str = QuasiQuoter { quoteExp = stringE , quotePat = undefined , quoteType = undefined , quoteDec = undefined }
これはこんな感じで使います。
StrTest.hs
{-# LANGUAGE QuasiQuotes #-} import Str main :: IO () main = putStr [str|こんにちは こんにちは こんばんは|]
実行。
% ghc StrTest.hs % ./StrTest こんにちは こんにちは こんばんは
変数の埋め込みとかはできませんが、やりたい方はruiccさんのブログとか読むといいんじゃないでしょうか。
続・Template Haskell入門 -- QuasiQuotes編 - think and error
TemplateHaskellに負けないで!
私はとりあえずいいや。
入力を受け取る
getInputを使います。
{-# LANGUAGE QuasiQuotes #-} import Network.CGI import Str contents :: String -> String contents body = header ++ body ++ footer where header = [str|<html> <body> <form action="." method="POST"> <input type="text" name="moge"> <input type="submit"> </form> |] footer = "</body></html>" cgiMain :: CGI CGIResult cgiMain = do setHeader "Content-type" "text/html" moge <- getInput "moge" output $ contents $ maybe "" id moge main :: IO () main = runCGI (handleErrors cgiMain)
これで、フォームに入れた文字がフォームの下に出たり出なかったり。
やっぱりテンプレートエンジン使ったほうがいいような気がします。