我正在開發一個棋盤游戲,每個團隊必須每回合提交一個命令。為了防止濫用,我正在嘗試創建一個登錄頁面,您可以在其中選擇一個團隊,提供團隊的密碼,然后繼續下一頁。
我正在使用 Haskell,使用此處提供的資源,特別是“獲取用戶輸入”部分。
相關檔案:Network.CGI , Text.XHtml
匯入、相關資料/型別和頁面功能:
import Network.CGI
import Text.XHtml
data Team = Team
{teamID :: Int,
teamName :: String} deriving Eq
type Lang = Int
type Teams = [Team]
page :: String -> Html -> Html
page t b = header << thetitle << t body << b
我有以下 loginPage 功能:
loginPage :: Lang -> Teams -> Html
loginPage lang teams = page (["Lépés Bejelentkezés", "Turn Login"] !! lang) $
form ! [method "post"] <<
-- [paragraph << (["Csapat: ", "Team: "] !! lang (select ! [name "teamID"] << teamOpts)), -- Generated version, does not work
{- -}
[paragraph << (["Csapat: ", "Team: "] !! lang
(select ! [name "teamID"] <<
[option ! [value "0"] << "Anglia", option ! [value "1"] << "Franciaország"])), --} -- Manually typed version, works perfectly
paragraph << (["Jelszó: ", "Password: "] !! lang password "password"),
submit "" (["Tovább", "Next"] !! lang) ]
where
teamOpts = map (\t -> option ! [value . show $ teamID t] << teamName t) $ teams
注釋行使用 teamOpts 生成選項串列,并將它們放在選擇標簽中,名稱為“teamID”。在當前未注釋的行中,我寫了(部分)我在呼叫 teamOpts 時期望的串列。
在 ghci 中,兩種方法都生成完全相同的 HTML。然而,當在我的網路服務器(Rocky Linux 上的 Apache)上呼叫這個程式時,我得到以下輸出:
- 生成:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><head
><title
>Lépés Bejelentkezés</t
- 手動撰寫:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><head
><title
>Lépés Bejelentkezés</title
></head
><body
><form method="post"
><p
>Csapat: <select name="teamID"
><option value="0"
>Anglia</option
><option value="1"
>Franciaország</option
></select
></p
><p
>Jelszó: <input type="password" name="password" id="password"
/></p
><input type="submit" value="Tovább"
/></form
></body
></html
>
如您所見,生成的版本甚至在完成標題標簽之前就終止了。
在命令列(在網路服務器上)運行腳本時,我得到了預期的結果(與手動撰寫的相同),帶有 CGI Header: Content-type: text/html; charset=ISO-8859-1。我也將它設定為Content-type: text/html; charset=UTF-8,但同樣的問題仍然存在。
我嘗試過的其他事情:
在創建串列之前使用 (teams
seq),以及其他強制評估的方法(通常程式在回傳后終止<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/嘗試使用更簡單的生成器進行除錯:(在 cli 中完美運行,但在加載時不能,回傳到 </t)
showTeams :: Teams -> Html
showTeams teams = page "Teams" $
paragraph << (concat . map (\t -> teamName t ", ") $ teams)
嘗試使用嚴格版本的 IO 函式(如此處推薦)。在 cli 中作業,但
hGetContents: invalid argument (invalid byte sequence)在網路上給出了錯誤。確保目錄中的所有內容都歸 apache:apache 所有
必要的代碼(newUnitsPage、cgiMain 和 main):
newUnitsPage :: Lang -> Teams -> Units -> Maybe String -> Maybe String -> Html
newUnitsPage lang teams units tid passwd = page (["új egységek", "New units"] !! lang) body
where
body = paragraph << "PLACEHOLDER" paragraph << fromJust tid paragraph << fromJust passwd
cgiMain = do
-- General setup
liftIO $ hSetEncoding stdin utf8 -- This doesn't change anything either
-- (What I use) {- -
paths' <- liftIO $ listDirectory "./"
let mapPaths = sort $ filter (=~ "\\.hmap$") paths'
hmap <- liftIO $ getNewestMap mapPaths
let teams = fetchTeams hmap --}
{- For your convenience:
Write the following to a file named "test.hmap":
Team {teamID = 0, teamName = "Anglia"}
Team {teamID = 1, teamName = "Franciaország"}
-}
test <- readFile "test.hmap" -- Pretty sure this is where it all goes wrong, but strict reading (Sysem.IO.Strict) does not fix it
let teams = map (\line -> read line :: Team) . lines $ test
-- Defaults to 0 (Hungarian)
mlang <- getInput "lang"
let lang = maybe 0 (\l -> if l `elem` ["1", "en"] then 1 else 0) mlang
-- All Inputs
-- Authentication
tid <- getInput "teamID"
password <- getInput "password"
newUnitOrders <- getInput "newUnitOrders" -- This is for the next page, not yet implemented, since login doesn't work yet.
-- Number coding for which form to show - method to show certain form based on what inputs exist
let code = fromJust $ foldM (\lastCode (mInput, code) -> if isNothing mInput then Just lastCode else Just code)
0 -- If username / password is not supplied, be on login page
[(tid,1),(password,1), -- If newUnitOrders are not supplied, be on newUnit page
(newUnitOrders,2)] -- Etc.
-- The html output
let pages =
[loginPage lang teams,
-- [showTeams teams,
newUnitsPage lang teams units tid password]
setHeader "Content-type" "text/html; charset=UTF-8" -- Optional
output . renderHtml $ pages !! code
main = runCGI $ handleErrors cgiMain
我在檔案中一遍又一遍地檢查,我沒有發現出現什么問題的跡象。
謝謝你的幫助!
uj5u.com熱心網友回復:
我能夠復制你的test.hmap版本。檢查服務器錯誤日志,您應該會在 hmap 檔案中看到有關無效位元組序列的注釋,內容如下:
AH01215: xhtml.cgi: test.hmap: hGetContents: invalid argument (invalid byte sequence): /usr/lib/cgi-bin/xhtml.cgi
問題似乎是 Apache 使用 運行 CGI 腳本LANG=C,并且當 Haskell 腳本從您的一個 hmap 檔案中讀取 unicode 資料時,它會在某個隨機時間點終止。使評估更嚴格可能會導致腳本更早地失敗,但這并不能解決問題。
最簡單的解決方法可能是添加:
liftIO $ setLocaleEncoding utf8 -- from GHC.IO.Encoding
到你的cgiMain函式的頂部。stdin(在我的測驗中不需要更改編碼。)
這是我用于測驗的腳本的完整版本。沒有這liftIO $ setLocaleEncoding utf8條線,它會以與您觀察到的完全相同的方式截斷輸出;使用該行,它可以正常作業:
import Control.Monad
import Data.Maybe
import Data.List
import System.Directory
import System.IO
import Network.CGI
import Text.XHtml
import GHC.IO.Encoding
data Team = Team
{teamID :: Int,
teamName :: String} deriving (Read, Eq)
type Lang = Int
type Teams = [Team]
page :: String -> Html -> Html
page t b = header << thetitle << t body << b
loginPage :: Lang -> Teams -> Html
loginPage lang teams = page (["Lépés Bejelentkezés", "Turn Login"] !! lang) $
form ! [method "post"] <<
[paragraph << (["Csapat: ", "Team: "] !! lang (select ! [name "teamID"] << teamOpts)), -- Generated version, does not work
paragraph << (["Jelszó: ", "Password: "] !! lang password "password"),
submit "" (["Tovább", "Next"] !! lang) ]
where
teamOpts = map (\t -> option ! [value . show $ teamID t] << teamName t) $ teams
cgiMain :: CGI CGIResult
cgiMain = do
liftIO $ setLocaleEncoding utf8
paths' <- liftIO $ listDirectory "./"
test <- liftIO $ readFile "test.hmap" -- Pretty sure this is where it all goes wrong, but strict reading (Sysem.IO.Strict) does not fix it
let teams = map (\line -> read line :: Team) . lines $ test
-- Defaults to 0 (Hungarian)
mlang <- getInput "lang"
let lang = maybe 0 (\l -> if l `elem` ["1", "en"] then 1 else 0) mlang
-- All Inputs
-- Authentication
tid <- getInput "teamID"
password <- getInput "password"
newUnitOrders <- getInput "newUnitOrders" -- This is for the next page, not yet implemented, since login doesn't work yet.
-- Number coding for which form to show - method to show certain form based on what inputs exist
let code = fromJust $ foldM (\lastCode (mInput, code) -> if isNothing mInput then Just lastCode else Just code)
0 -- If username / password is not supplied, be on login page
[(tid,1),(password,1), -- If newUnitOrders are not supplied, be on newUnit page
(newUnitOrders,2)] -- Etc.
-- The html output
let pages =
[loginPage lang teams]
-- [showTeams teams,
-- newUnitsPage lang teams units tid password]
setHeader "Content-type" "text/html; charset=UTF-8" -- Optional
output . renderHtml $ pages !! code
main :: IO ()
main = runCGI $ cgiMain
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/510595.html
