Yesod web 框架/控制器
Yesod 使用 Web 应用程序接口 API,[1] 简称 WAI,将 servlet(即 web 应用程序)与服务器隔离开,并为服务器协议 CGI,[2] FastCGI,[3] SCGI,[4] Warp,[5] Launch(将本地 URL 打开到默认浏览器,在窗口关闭时关闭服务器)[6] 提供处理程序。
参见 ref.[7] Yesod 需要一个数据类型来实例化控制器类。这称为 基础 类型。在下面的示例中,它名为 "MyApp"。
REST 模型通过 Web 路径标识 Web 资源。这里,REST 资源使用带有 R 后缀的名称(例如 "HomeR")来命名,并在 parseRoutes 站点地图描述模板中列出。从这个列表中,派生路由名称和调度处理程序名称。
Yesod 利用模板 Haskell 元编程在编译时从模板生成代码,确保模板中的名称匹配并且所有内容都经过类型检查(例如,Web 资源名称和处理程序名称)。
通过插入 mkYesod 调用,这将调用模板 Haskell 原语来生成与路由类型成员相对应的代码[8],以及调度控制器类的实例,以便将 GET 调用调度到路由 HomeR 到一个名为将两者组合起来作为 "getHomeR" 的例程,期望一个与名称匹配的现有处理程序。
"Hello world" 示例基于 CGI 服务器接口(实际处理程序类型已经改变,但理念保持不变)
{- file wai-cgi-hello.hs -}
{-# LANGUAGE PackageImports, TypeFamilies, QuasiQuotes, MultiParamTypeClasses,
TemplateHaskell, OverloadedStrings #-}
import "wai" Network.Wai
import "wai-extra" Network.Wai.Handler.CGI (run) -- interchangeable WAI handler
import "yesod" Yesod
import "yesod-core" Yesod.Handler (getRequest)
import "text" Data.Text (Text)
import "shakespeare" Text.Cassius (Color(..), colorBlack)
-- the Foundation type
data MyApp = MyApp
-- sitemap template, listing path, resource name and methods accepted
-- `mkYesod` takes the foundation type name as param. for name composition of dispatch functions
mkYesod "MyApp" [parseRoutes|
/ HomeR GET
|]
instance Yesod MyApp
-- indentation structured CSS template
myStyle :: [Text] → CssUrl url
myStyle paramStyle =
[cassius|
.box
border: 1px solid #{boxColor}
|]
where
boxColor = case paramStyle of
["high-contrast"] → colorBlack
_ → Color 0 0 255
-- indentation structured HTML template
myHtml :: [(Text, Text)] → HtmlUrl url
myHtml params = [hamlet|
<!-- indentation, or lack of it, under starting tags or commands ('$' prefix)
describe the content or sequence tree structure -->
<!-- '.' or '#' prefixes in tags introduce css styled "class" or "id" attribute values -->
<!-- interpolation of haskell expressions follow the "shakespeare templates" #{expr} syntax -->
<p>Hello World! There are <span .box>#{length params} parameters</span>:
$if null params
<p>Nothing to list
$else
<ul>
$forall param <- params
<li>#{fst param}: #{snd param}
|]
getHomeR :: Handler RepHtml
getHomeR = do
req <- getRequest
let params = reqGetParams req
paramStyle <- lookupGetParams "style"
defaultLayout $ do
-- adding widgets to the Widget monad (a ''Writer'' monad)
setTitle "Yesod example"
toWidgetHead $ myStyle paramStyle
toWidgetBody $ myHtml params
-- there are ''run'' function variants for different WAI handlers
main = toWaiApp MyApp >>= run
# cgi test
export REMOTE_ADDR=127.0.0.1
export REQUEST_METHOD=GET
export PATH_INFO=/
export QUERY_STRING='p1=abc;p2=def;style=high-contrast'
./wai-cgi-hello
参见 ref.[9][10] Yesod 遵循访问 Web 文档的 REpresentational State Transfer 模型,使用路由构造函数识别文档和目录作为资源,并使用大写 R 后缀命名(例如,HomeR)。
- 路由表
- parseRoutes 模板应列出指定路由片段、资源名称和要接受的调度方法的资源。
URL 段捕获作为参数是可能的,指定 '#' 前缀用于单段捕获,或 '*' 用于多段捕获,后跟参数类型。
-- given a MyApp foundation type
mkYesod "MyApp" [parseRoutes|
/ HomeR -- no http methods stated: all methods accepted
/blog BlogR GET POST
-- the '#' prefix specify the path segment as a route handler parameter
/article/#ArticleId ArticleR GET PUT
-- the '*' prefix specify the parameter as a sequence of path pieces
/branch/*Texts BranchR GET
-- to simplify the grammar, compound types must use an alias, eg. type Texts for ''[Text]''
|]
- 应用前面的模板生成以下路由构造函数
data Route MyApp =
HomeR -- referenced in templates as: @{HomeR}
| BlogR -- in templates: @{BlogR}
| ArticleR ArticleId -- in templates: @{ArticleR myArticleId}
| BranchR Texts -- in templates: @{BranchR myBranchSegments}
- 对于每种支持的 HTTP 方法,必须创建一个处理程序函数来匹配 mkYesod 从 parseRoutes 模板生成的调度名称,方法是将方法名称(如果未指定方法,则使用前缀 "handler")附加到资源,如所描述的那样(实际版本处理程序类型已经改变,但理念保持不变)
-- for "/ HomeR" -- no http methods stated ⇒ only one handler with prefix ''handler''
handlerHomeR :: HasReps t ⇒ Handler t
-- for "/blog BlogR GET POST"
getBlogR :: HasReps t ⇒ Handler t
postBlogR :: HasReps t ⇒ Handler t
-- for "/article/#ArticleId ArticleR GET PUT"
getArticleR :: HasReps t ⇒ ArticleId → Handler t
putArticleR :: HasReps t ⇒ ArticleId → Handler t
参见 ref.[9]
参见 ref.[11] 身份验证插件:OpenId、BrowserId、电子邮件、Google 电子邮件、HashDB、RpxNow.[12]
- 身份验证后自动重定向有一个重要的设置。[13]
参见 ref.[14] 会话后端:ClientSession[15](它将会话存储在 Cookie 中)、ServerSession[16][17](它将大部分会话数据存储在服务器上)
- >> 为了避免不必要的带宽开销,生产站点可以从一个单独的域名提供静态内容,以避免为每个请求传输会话 Cookie 的开销。
可以在会话中存储成功、失败或指示性消息(setMessage),如果存在,则通过 default_layout.hamlet
模板,由 default_layout 例程显示,并在咨询时清除。[18]
用于工作流、文件服务或站点分区之类的通用 URL 前缀子站点。参见 ref.[19][20]
内置子站点:Static,[21][22] Auth[23]
参见 ref.[24]
这里的 Form 类型是一个对象,它在 controller 中使用,用于解析和处理表单字段用户输入,并生成一个 (FormResult, Widget) 对,其中小部件包含表单的下一个渲染的布局,包括错误消息和标记。它还可用于生成带有空白或默认值的新表单。
表单类型采用一个要嵌入到视图中的 html 片段的函数的形式,该片段将包含出于安全目的的隐藏字段。
表单对象是从字段的 Applicative/Monadic 组合生成的,用于对字段输入进行组合/顺序解析。
有三种类型的表单
- Applicative(带有表格布局),
- Monadic(带有自由布局样式),两者都在 Yesod.Form.Functions 模块中,
- Input(仅用于解析,不生成视图)位于 Yesod.Form.Input 模块中。
字段生成器,其名称由表单类型初始 (a|m|i)
后跟 (req|opt){- 必需或可选 -}
组成,具有一个 fieldParse 组件和一个 fieldView 组件。[25]
- 函数 runForm{Post|Get} 会对表单字段输入运行字段解析器,并从视图中生成一个 (FormResult, Widget) 对,提供一个新的表单小部件,其中接收到的表单字段值作为默认值。函数后缀是表单提交中使用的 HTTP 方法。
- 而 generateForm{Post|Get} 会忽略来自客户端的输入,并生成一个空白或默认表单小部件。[26]
实际的函数参数和类型在 Yesod 版本中有所改变。请查看 Yesod 手册和库签名。
神奇之处在于 FormResult 数据类型 Applicative 实例,其中 (<*>) 收集了 FormFailure [textErrMsg]
结果值的错误消息。[27]
单子形式允许自由的表单布局和对 hiddenField 成员的更好处理。[24]
一个 Applicative[28] 表单的示例
-- a record for our form fields
data Person = Person {personName :: Text, personAge :: Int, personLikings :: Maybe Text}
-- the Form type has an extra parameter for an html snippet to be embedded, containing a CSRF token hidden field for security
type Form sub master x = Html → MForm sub master (FormResult x, Widget)
{-
-- for messages in validation functions:
@param master: yesod instance to use in renderMessage (return from handler's getYesod)
@param languages: page languages to use in renderMessage
-- optional defaults record:
@param mbPersonDefaults: Just defaults_record, or Nothing for blank form
-}
personForm :: MyFoundationType → [Text] → Maybe Person → Form sub master Person
{- ''aopt'' (optional field AForm component) for "Maybe" fields,
''areq'' (required fld AForm comp.) will insert the "required" attribute
-}
personForm master languages mbPersonDefaults = renderTable $
Person <$> areq textField fldSettingsName mbNameDefault
<*> areq customPersonAgeField fldSettingsAge mbAgeDefault
<*> aopt textareaField fldSettingsLikings mbLikingsDefault
where
mbNameDefault = fmap personName mbPersonDefaults
mbAgeDefault = fmap personAge mbPersonDefaults
mbLikingsDefault = fmap personLikings mbPersonDefaults
-- "fieldSettingsLabel" returns an initial fieldSettings record
-- recently the "FieldSettings" record can be defined from a String label since it implements IsString
fldSettingsName = (fieldSettingsLabel MsgName) {fsAttrs = [("maxlength","20")]}
fldSettingsAge = fieldSettingsLabel MsgAge
fldSettingsLikings = (fieldSettingsLabel MsgLikings) {fsAttrs = [("cols","40"),("rows","10")]}
customPersonAgeField = check validateAge intField
validateAge y
| y < 18 = Left $ renderMessage master languages MsgUnderAge
| otherwise = Right y
本页部分内容基于以下材料 维基百科: 免费的百科全书. |
- ↑ "The wai package". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "The wai-extra package with CGI WAI handler". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "The wai-handler-fastcgi package". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "The wai-handler-scgi package". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "The warp package". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "The wai-handler-launch package". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ a b "book - Basics". Yesodweb.com. Retrieved 2012-10-23.
- ↑ The mkYesod code
- ↑ a b "book - Routing and Handlers". Yesodweb.com. Retrieved 2012-10-23.
- ↑ "Playing with Routes and Links". FPComplete.com. 2012-10-17. Retrieved 2012-10-28.
- ↑ "book - Authentication and Authorization". Yesodweb.com. Retrieved 2012-10-23.
- ↑ "The yesod-auth package". Hackage.haskell.org. Retrieved 2012-10-26.
- ↑ "book - Sessions - See section "Ultimate Destination"". Yesodweb.com. Retrieved 2012-11-17.
- ↑ "Sessions". Yesodweb.com. Retrieved 2012-10-23.
- ↑ "Web.ClientSession". Hackage.haskell.org. Retrieved 2012-10-25.
- ↑ "ServerSession: secure modular server-side sessions". Hackage.haskell.org. Retrieved 2018-10-29.
- ↑ "Web.ServerSession.Frontend.Yesod". Hackage.haskell.org. Retrieved 2018-10-29.
- ↑ "Session Messages". Yesodweb.com. Retrieved 2018-10-23.
- ↑ "创建子站点". Yesodweb.com. Retrieved 2012-10-25.
- ↑ "Yesod 和子站点:轻而易举". Monoid.se. 2012-08-22. Retrieved 2012-10-28.[]
- ↑ "Yesod 的魔力,第二部分 - 请参见“静态子站点”部分". Yesodweb.com. 2010-12-25. Retrieved 2012-10-25.
- ↑ "yesod-static 包 - 静态子站点". Hackage.haskell.org. Retrieved 2012-10-25.
- ↑ "yesod-auth 包 - 认证子站点". Hackage.haskell.org. Retrieved 2012-10-25.
- ↑ a b "书籍 - 表单". Yesodweb.com. Retrieved 2012-10-23.
- ↑ "Yesod.Form.Fields". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "Yesod.Form.Functions runFormPost". Hackage.haskell.org. Retrieved 2012-10-25.
- ↑ "Yesod.Form.Types". Hackage.haskell.org. Retrieved 2012-10-23.
- ↑ "HaskellWiki - 可应用函子". haskell.org. Retrieved 2012-10-24.