mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-01-16 04:07:13 +08:00
rebuild
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<title>獲取結構體字段標識 | 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">
|
||||
<meta name="generator" content="GitBook 2.6.6">
|
||||
|
||||
|
||||
<meta name="HandheldFriendly" content="true"/>
|
||||
@@ -48,7 +48,13 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.7" data-chapter-title="獲取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Thu Dec 31 2015 16:18:40 GMT+0800 (中国标准时间)">
|
||||
<div class="book"
|
||||
data-level="12.7"
|
||||
data-chapter-title="獲取結構體字段標識"
|
||||
data-filepath="ch12/ch12-07.md"
|
||||
data-basepath=".."
|
||||
data-revision="Sat Jan 02 2016 16:00:23 GMT+0800 (中国标准时间)"
|
||||
data-innerlanguage="">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -2024,7 +2030,122 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="127-獲取結構體字段標識">12.7. 獲取結構體字段標識</h2>
|
||||
<p>TODO</p>
|
||||
<p>在4.5節我們使用構體成員標籤用於設置對應JSON對應的名字。其中json成員標籤讓我們可以選擇成員的名字和抑製零值成員的輸出。在本節,我們將看到如果通過反射機製類獲取成員標籤。</p>
|
||||
<p>對於一個web服務,大部分HTTP處理函數要做的第一件事情就是展開請求中的參數到本地變量中。我們定義了一個工具函數,叫params.Unpack,通過使用結構體成員標籤機製來讓HTTP處理函數解析請求參數更方便。</p>
|
||||
<p>首先,我們看看如何使用它。下面的search函數是一個HTTP請求處理函數。它定義了一個匿名結構體類型的變量,用結構體的每個成員表示HTTP請求的參數。其中結構體成員標籤指明了對於請求參數的名字,爲了減少UTRL的長度這些參數名通常都是神祕的縮略詞。Unpack將請求參數填充到合適的結構體成員中,這樣我們可以方便地通過合適的類型類來訪問這些參數。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch12/search
|
||||
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"gopl.io/ch12/params"</span>
|
||||
|
||||
<span class="hljs-comment">// search implements the /search URL endpoint.</span>
|
||||
<span class="hljs-keyword">func</span> search(resp http.ResponseWriter, req *http.Request) {
|
||||
<span class="hljs-keyword">var</span> data <span class="hljs-keyword">struct</span> {
|
||||
Labels []<span class="hljs-typename">string</span> <span class="hljs-string">`http:"l"`</span>
|
||||
MaxResults <span class="hljs-typename">int</span> <span class="hljs-string">`http:"max"`</span>
|
||||
Exact <span class="hljs-typename">bool</span> <span class="hljs-string">`http:"x"`</span>
|
||||
}
|
||||
data.MaxResults = <span class="hljs-number">10</span> <span class="hljs-comment">// set default</span>
|
||||
<span class="hljs-keyword">if</span> err := params.Unpack(req, &data); err != <span class="hljs-constant">nil</span> {
|
||||
http.Error(resp, err.Error(), http.StatusBadRequest) <span class="hljs-comment">// 400</span>
|
||||
<span class="hljs-keyword">return</span>
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// ...rest of handler...</span>
|
||||
fmt.Fprintf(resp, <span class="hljs-string">"Search: %+v\n"</span>, data)
|
||||
}
|
||||
</code></pre>
|
||||
<p>下面的Unpack函數主要完成三件事情。第一,它調用req.ParseForm()來解析HTTP請求。然後,req.Form將包含所有的請求參數,不管HTTP客戶端使用的是GET還是POST請求方法。</p>
|
||||
<p>下一步,Unpack函數將構建每個結構體成員有效參數名字到成員變量的映射。如果結構體成員有成員標籤的話,有效參數名字可能和實際的成員名字不相同。reflect.Type的Field方法將返迴一個reflect.StructField,里面含有每個成員的名字、類型和可選的成員標籤等信息。其中成員標籤信息對應reflect.StructTag類型的字符串,併且提供了Get方法用於解析和根據特定key提取的子串,例如這里的http:"..."形式的子串。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch12/params
|
||||
|
||||
<span class="hljs-comment">// Unpack populates the fields of the struct pointed to by ptr</span>
|
||||
<span class="hljs-comment">// from the HTTP request parameters in req.</span>
|
||||
<span class="hljs-keyword">func</span> Unpack(req *http.Request, ptr <span class="hljs-keyword">interface</span>{}) error {
|
||||
<span class="hljs-keyword">if</span> err := req.ParseForm(); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// Build map of fields keyed by effective name.</span>
|
||||
fields := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]reflect.Value)
|
||||
v := reflect.ValueOf(ptr).Elem() <span class="hljs-comment">// the struct variable</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < v.NumField(); i++ {
|
||||
fieldInfo := v.Type().Field(i) <span class="hljs-comment">// a reflect.StructField</span>
|
||||
tag := fieldInfo.Tag <span class="hljs-comment">// a reflect.StructTag</span>
|
||||
name := tag.Get(<span class="hljs-string">"http"</span>)
|
||||
<span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span> {
|
||||
name = strings.ToLower(fieldInfo.Name)
|
||||
}
|
||||
fields[name] = v.Field(i)
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// Update struct field for each parameter in the request.</span>
|
||||
<span class="hljs-keyword">for</span> name, values := <span class="hljs-keyword">range</span> req.Form {
|
||||
f := fields[name]
|
||||
<span class="hljs-keyword">if</span> !f.IsValid() {
|
||||
<span class="hljs-keyword">continue</span> <span class="hljs-comment">// ignore unrecognized HTTP parameters</span>
|
||||
}
|
||||
<span class="hljs-keyword">for</span> _, value := <span class="hljs-keyword">range</span> values {
|
||||
<span class="hljs-keyword">if</span> f.Kind() == reflect.Slice {
|
||||
elem := reflect.New(f.Type().Elem()).Elem()
|
||||
<span class="hljs-keyword">if</span> err := populate(elem, value); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"%s: %v"</span>, name, err)
|
||||
}
|
||||
f.Set(reflect.Append(f, elem))
|
||||
} <span class="hljs-keyword">else</span> {
|
||||
<span class="hljs-keyword">if</span> err := populate(f, value); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"%s: %v"</span>, name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>最後,Unpack遍歷HTTP請求的name/valu參數鍵值對,併且根據更新相應的結構體成員。迴想一下,同一個名字的參數可能出現多次。如果發生這種情況,併且對應的結構體成員是一個slice,那麽就將所有的參數添加到slice中。其它情況,對應的成員值將被覆蓋,隻有最後一次出現的參數值才是起作用的。</p>
|
||||
<p>populate函數小心用請求的字符串類型參數值來填充單一的成員v(或者是slice類型成員中的單一的元素)。目前,它僅支持字符串、有符號整數和布爾型。其中其它的類型將留做練習任務。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> populate(v reflect.Value, value <span class="hljs-typename">string</span>) error {
|
||||
<span class="hljs-keyword">switch</span> v.Kind() {
|
||||
<span class="hljs-keyword">case</span> reflect.String:
|
||||
v.SetString(value)
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Int:
|
||||
i, err := strconv.ParseInt(value, <span class="hljs-number">10</span>, <span class="hljs-number">64</span>)
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
v.SetInt(i)
|
||||
|
||||
<span class="hljs-keyword">case</span> reflect.Bool:
|
||||
b, err := strconv.ParseBool(value)
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> err
|
||||
}
|
||||
v.SetBool(b)
|
||||
|
||||
<span class="hljs-keyword">default</span>:
|
||||
<span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"unsupported kind %s"</span>, v.Type())
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>如果我們上上面的處理程序添加到一個web服務器,則可以産生以下的會話:</p>
|
||||
<pre><code>$ go build gopl.io/ch12/search
|
||||
$ ./search &
|
||||
$ ./fetch 'http://localhost:12345/search'
|
||||
Search: {Labels:[] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming&max=100'
|
||||
Search: {Labels:[golang programming] MaxResults:100 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?x=true&l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:true}
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&x=123'
|
||||
x: strconv.ParseBool: parsing "123": invalid syntax
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&max=lots'
|
||||
max: strconv.ParseInt: parsing "lots": invalid syntax
|
||||
</code></pre><p><strong>練習 12.11:</strong> 編寫相應的Pack函數,給定一個結構體值,Pack函數將返迴合併了所有結構體成員和值的URL。</p>
|
||||
<p><strong>練習 12.12:</strong> 擴展成員標籤以表示一個請求參數的有效值規則。例如,一個字符串可以是有效的email地址或一個信用卡號碼,還有一個整數可能需要是有效的郵政編碼。脩改Unpack函數以檢査這些規則。</p>
|
||||
<p><strong>練習 12.13:</strong> 脩改S表達式的編碼器(§12.4)和解碼器(§12.6),采用和encoding/json包(§4.5)類似的方式使用成員標籤中的sexpr:"..."字串。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user