mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-01-17 04:37:14 +08:00
rebuild
This commit is contained in:
129
ch4/ch4-06.html
129
ch4/ch4-06.html
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>文本和HTML模闆 | Go编程语言</title>
|
||||
<title>文本和HTML模闆 | Go语言圣经</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Thu Dec 31 2015 16:18:40 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -575,7 +575,7 @@
|
||||
|
||||
<b>4.2.</b>
|
||||
|
||||
切片
|
||||
Slice
|
||||
</a>
|
||||
|
||||
|
||||
@@ -590,7 +590,7 @@
|
||||
|
||||
<b>4.3.</b>
|
||||
|
||||
字典
|
||||
Map
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2013,7 +2013,7 @@
|
||||
<!-- Title -->
|
||||
<h1>
|
||||
<i class="fa fa-circle-o-notch fa-spin"></i>
|
||||
<a href="../" >Go编程语言</a>
|
||||
<a href="../" >Go语言圣经</a>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -2024,7 +2024,124 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="46-文本和html模闆">4.6. 文本和HTML模闆</h2>
|
||||
<p>TODO</p>
|
||||
<p>前面的例子,隻是最簡單的格式,使用Printf是完全足夠的。但是有時候會需要複雜的打印格式,這時候一般需要將格式化代碼分離出來以便更安全地脩改。這寫功能是由text/template和html/template等模闆包提供的,它們提供了一個用變量值填充到一個文本或HTML格式的模闆的機製。</p>
|
||||
<p>一個模闆是一個字符串或一個文件,里面包含了一個或多個由雙花括號包含的action對象。大部分的字符串隻是按面值打印,但是對於actions部分將觸發其它的行爲。買個actions包好了一個用模闆語言書寫的表達式,一個雖然簡短但是可以輸出複雜的打印值,模闆語言包含通過選擇結構體的成員、調用函數或方法、表達式控製流if-else語句和range循環語句,還有其它實例化模闆等諸多特性。下面是一個簡單的模闆字符串:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch4/issuesreport
|
||||
|
||||
<span class="hljs-keyword">const</span> templ = <span class="hljs-string">`{{.TotalCount}} issues:
|
||||
{{range .Items}}----------------------------------------
|
||||
Number: {{.Number}}
|
||||
User: {{.User.Login}}
|
||||
Title: {{.Title | printf "%.64s"}}
|
||||
Age: {{.CreatedAt | daysAgo}} days
|
||||
{{end}}`</span>
|
||||
</code></pre>
|
||||
<p>這個模闆先打印匹配到的issue總數,然後打印每個issue的編號、創建用戶、標題還有存在的時間。每一個action,都有一個當前值的概念,對應點操作符,寫作“.”。當前值“.”最初被初始化爲調用模闆是的參數,在當前例子中對應github.IssuesSearchResult類型的變量。模闆中<code>{{.TotalCount}}</code>對應action將展開爲結構體中TotalCount成員以默認的方式打印的值。模闆中<code>{{range .Items}}</code>和<code>{{end}}</code>對應一個循環action,因此它們直接的內容可能會被展開多次,循環每次迭代的當前值對應當前的Items元素的值。</p>
|
||||
<p>在一個action中,<code>|</code>操作符表示將前一個表達式的結果作爲後一個函數的輸入,類似於UNIX中管道的概念。在Title這一行的action中,第二個操作是一個printf函數,是一個基於fmt.Sprintf實現的內置函數,所有模闆都可以直接使用。對於Age部分,第二個動作是一個叫daysAgo的函數,通過time.Since函數將CreatedAt成員轉換爲過去的時間長度:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> daysAgo(t time.Time) <span class="hljs-typename">int</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-typename">int</span>(time.Since(t).Hours() / <span class="hljs-number">24</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>需要註意的是CreatedAt的參數類型是time.Time,併不是字符串。以同樣的方式,我們可以通過定義一些方法來控製字符串的格式化(§2.5),一個類型同樣可以定製自己的JSON編碼和解碼行爲。time.Time類型對應的JSON值是一個標準時間格式的字符串。</p>
|
||||
<p>生成模闆的輸出需要兩個處理步驟。第一步是要分析模闆併轉爲內部表示,然後基於指定的輸入執行模闆。分析模闆部分一般隻需要執行一次。下面的代碼創建併分析上面定義的模闆templ。註意方法調用鏈的順序:template.New先創建併返迴一個模闆;Funcs方法將daysAgo等自定義函數註冊到模闆中,併返迴模闆;最後調用Parse函數分析模闆。</p>
|
||||
<pre><code class="lang-Go">report, err := template.New(<span class="hljs-string">"report"</span>).
|
||||
Funcs(template.FuncMap{<span class="hljs-string">"daysAgo"</span>: daysAgo}).
|
||||
Parse(templ)
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
log.Fatal(err)
|
||||
}
|
||||
</code></pre>
|
||||
<p>因爲模闆通常在編譯時就測試好了,如果模闆解析失敗將是一個致命的錯誤。template.Must輔助函數可以簡化這個致命錯誤的處理:它接受一個模闆和一個error類型的參數,檢測error是否爲nil(如果不是則發出panic異常),然後返迴傳入的模闆。我們將在5.9節再討論這個話題。</p>
|
||||
<p>一旦模闆已經創建、註冊了daysAgo函數、併通過分析和檢測,我們就可以使用github.IssuesSearchResult作爲輸入源、os.Stdout作爲輸出源來執行模闆:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> report = template.Must(template.New(<span class="hljs-string">"issuelist"</span>).
|
||||
Funcs(template.FuncMap{<span class="hljs-string">"daysAgo"</span>: daysAgo}).
|
||||
Parse(templ))
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
result, err := github.SearchIssues(os.Args[<span class="hljs-number">1</span>:])
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<span class="hljs-keyword">if</span> err := report.Execute(os.Stdout, result); err != <span class="hljs-constant">nil</span> {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>程序輸出一個純文本報告:</p>
|
||||
<pre><code>$ go build gopl.io/ch4/issuesreport
|
||||
$ ./issuesreport repo:golang/go is:open json decoder
|
||||
13 issues:
|
||||
----------------------------------------
|
||||
Number: 5680
|
||||
User: eaigner
|
||||
Title: encoding/json: set key converter on en/decoder
|
||||
Age: 750 days
|
||||
----------------------------------------
|
||||
Number: 6050
|
||||
User: gopherbot
|
||||
Title: encoding/json: provide tokenizer
|
||||
Age: 695 days
|
||||
----------------------------------------
|
||||
...
|
||||
</code></pre><p>現在讓我們轉到html/template模闆包。它使用和text/template包相同的API和模闆語言,但是增加了一個將字符串自動轉義,以避免輸入字符串和HTML、JavaScript、CSS或URL語法産生衝突的問題。這個特性可以避免一些長期存在的安全問題,比如通過生成HTML註入攻擊,通過構造一個含有惡意代碼的問題標題,這些都可能讓模闆輸出錯誤的輸出,從而讓他們控製頁面。</p>
|
||||
<p>下面的模闆以HTML格式輸出issue列表。註意import語句的不同:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch4/issueshtml
|
||||
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"html/template"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> issueList = template.Must(template.New(<span class="hljs-string">"issuelist"</span>).Parse(<span class="hljs-string">`
|
||||
<h1>{{.TotalCount}} issues</h1>
|
||||
<table>
|
||||
<tr style='text-align: left'>
|
||||
<th>#</th>
|
||||
<th>State</th>
|
||||
<th>User</th>
|
||||
<th>Title</th>
|
||||
</tr>
|
||||
{{range .Items}}
|
||||
<tr>
|
||||
<td><a href='{{.HTMLURL}}'>{{.Number}}</td>
|
||||
<td>{{.State}}</td>
|
||||
<td><a href='{{.User.HTMLURL}}'>{{.User.Login}}</a></td>
|
||||
<td><a href='{{.HTMLURL}}'>{{.Title}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
`</span>))
|
||||
</code></pre>
|
||||
<p>下面的命令將在新的模闆上執行一個稍微不同的査詢:</p>
|
||||
<pre><code class="lang-Go">$ <span class="hljs-keyword">go</span> build gopl.io/ch4/issueshtml
|
||||
$ ./issueshtml repo:golang/<span class="hljs-keyword">go</span> commenter:gopherbot json encoder >issues.html
|
||||
</code></pre>
|
||||
<p>圖4.4顯示了在web瀏覽器中的效果圖。每個issue包含到Github對應頁面的鏈接。</p>
|
||||
<p><img src="../images/ch4-04.png" alt=""></p>
|
||||
<p>圖4.4中的沒有問題會對HTML格式産生衝突,但是我們馬上將看到標題中含有<code>&</code>和<code><</code>字符的issue。下面的命令選擇了兩個這樣的issue:</p>
|
||||
<pre><code>$ ./issueshtml repo:golang/go 3133 10535 >issues2.html
|
||||
</code></pre><p>圖4.5顯示了該査詢的結果。註意,html/template包已經自動將特殊字符轉義,我們依然可以看到正確的字面值。如果我們使用text/template包的話,這2個issue將會産生錯誤,其中“<”四個字符將會被當作小於字符“<”處理,同時“<link>”字符串將會被當作一個鏈接元素處理,它們都會導致HTML文檔結構的改變,從而導致有未知的風險。</p>
|
||||
<p>我們也可以通過對信任的HTML字符串使用template.HTML類型來抑製這種自動轉義的行爲。還有很多采用類型命名的字符串類型對應信任的JavaScript、CSS和URL。下面的程序演示了兩個使用不同類型的相同字符串産生的不同結果:A是一個普通字符串,B是一個信任的template.HTML字符串類型。</p>
|
||||
<p><img src="../images/ch4-05.png" alt=""></p>
|
||||
<pre><code class="lang-Go">gopl.io/ch4/autoescape
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
<span class="hljs-keyword">const</span> templ = <span class="hljs-string">`<p>A: {{.A}}</p><p>B: {{.B}}</p>`</span>
|
||||
t := template.Must(template.New(<span class="hljs-string">"escape"</span>).Parse(templ))
|
||||
<span class="hljs-keyword">var</span> data <span class="hljs-keyword">struct</span> {
|
||||
A <span class="hljs-typename">string</span> <span class="hljs-comment">// untrusted plain text</span>
|
||||
B template.HTML <span class="hljs-comment">// trusted HTML</span>
|
||||
}
|
||||
data.A = <span class="hljs-string">"<b>Hello!</b>"</span>
|
||||
data.B = <span class="hljs-string">"<b>Hello!</b>"</span>
|
||||
<span class="hljs-keyword">if</span> err := t.Execute(os.Stdout, data); err != <span class="hljs-constant">nil</span> {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>圖4.6顯示了出現在瀏覽器中的模闆輸出。我們看到A的黑體標記被轉義失效了,但是B沒有。</p>
|
||||
<p><img src="../images/ch4-06.png" alt=""></p>
|
||||
<p>我們這里隻講述了模闆繫統中最基本的特性。一如旣往,如果想了解更多的信息,請自己査看包文檔:</p>
|
||||
<pre><code>$ go doc text/template
|
||||
$ go doc html/template
|
||||
</code></pre><p><strong>練習 4.14:</strong> 創建一個web服務器,査詢一次GitHub,然後生成BUG報告、里程碑和對應的用戶信息。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user