diff --git a/404.html b/404.html index 4ea680e..03fef93 100644 --- a/404.html +++ b/404.html @@ -85,7 +85,7 @@ diff --git a/appendix/appendix-a-errata.html b/appendix/appendix-a-errata.html index b22c59a..5640cb9 100644 --- a/appendix/appendix-a-errata.html +++ b/appendix/appendix-a-errata.html @@ -84,7 +84,7 @@ diff --git a/appendix/appendix-b-author.html b/appendix/appendix-b-author.html index f0876a5..faa46f9 100644 --- a/appendix/appendix-b-author.html +++ b/appendix/appendix-b-author.html @@ -84,7 +84,7 @@ diff --git a/appendix/appendix-c-cpoyright.html b/appendix/appendix-c-cpoyright.html index 8855fa2..31ac515 100644 --- a/appendix/appendix-c-cpoyright.html +++ b/appendix/appendix-c-cpoyright.html @@ -84,7 +84,7 @@ diff --git a/appendix/appendix-d-translations.html b/appendix/appendix-d-translations.html index a3fbc3e..d571480 100644 --- a/appendix/appendix-d-translations.html +++ b/appendix/appendix-d-translations.html @@ -84,7 +84,7 @@ diff --git a/appendix/appendix.html b/appendix/appendix.html index a8bd5d9..24e66c6 100644 --- a/appendix/appendix.html +++ b/appendix/appendix.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-01.html b/ch1/ch1-01.html index 51f2536..3b486c4 100644 --- a/ch1/ch1-01.html +++ b/ch1/ch1-01.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-02.html b/ch1/ch1-02.html index 2dd6477..6784fd0 100644 --- a/ch1/ch1-02.html +++ b/ch1/ch1-02.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-03.html b/ch1/ch1-03.html index 06cdd0a..abce703 100644 --- a/ch1/ch1-03.html +++ b/ch1/ch1-03.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-04.html b/ch1/ch1-04.html index f94dac1..91a7c80 100644 --- a/ch1/ch1-04.html +++ b/ch1/ch1-04.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-05.html b/ch1/ch1-05.html index 989d77c..a80d4fe 100644 --- a/ch1/ch1-05.html +++ b/ch1/ch1-05.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-06.html b/ch1/ch1-06.html index a90dfb1..48c778b 100644 --- a/ch1/ch1-06.html +++ b/ch1/ch1-06.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-07.html b/ch1/ch1-07.html index c91f00e..0bb325a 100644 --- a/ch1/ch1-07.html +++ b/ch1/ch1-07.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1-08.html b/ch1/ch1-08.html index ccae393..a7c905a 100644 --- a/ch1/ch1-08.html +++ b/ch1/ch1-08.html @@ -84,7 +84,7 @@ diff --git a/ch1/ch1.html b/ch1/ch1.html index d565899..052360c 100644 --- a/ch1/ch1.html +++ b/ch1/ch1.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-01.html b/ch10/ch10-01.html index c41437c..42e742c 100644 --- a/ch10/ch10-01.html +++ b/ch10/ch10-01.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-02.html b/ch10/ch10-02.html index 89fa149..9cc7c90 100644 --- a/ch10/ch10-02.html +++ b/ch10/ch10-02.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-03.html b/ch10/ch10-03.html index 93a460b..37a238c 100644 --- a/ch10/ch10-03.html +++ b/ch10/ch10-03.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-04.html b/ch10/ch10-04.html index a8cf2bc..d4dea9e 100644 --- a/ch10/ch10-04.html +++ b/ch10/ch10-04.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-05.html b/ch10/ch10-05.html index f41388c..d0b388e 100644 --- a/ch10/ch10-05.html +++ b/ch10/ch10-05.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-06.html b/ch10/ch10-06.html index cd65638..e06ea3d 100644 --- a/ch10/ch10-06.html +++ b/ch10/ch10-06.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10-07.html b/ch10/ch10-07.html index fbc9d81..8de7ab6 100644 --- a/ch10/ch10-07.html +++ b/ch10/ch10-07.html @@ -84,7 +84,7 @@ diff --git a/ch10/ch10.html b/ch10/ch10.html index 1aee83d..23874ec 100644 --- a/ch10/ch10.html +++ b/ch10/ch10.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-01.html b/ch11/ch11-01.html index 4fc3993..1187839 100644 --- a/ch11/ch11-01.html +++ b/ch11/ch11-01.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-02.html b/ch11/ch11-02.html index f4b5824..6c25c4f 100644 --- a/ch11/ch11-02.html +++ b/ch11/ch11-02.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-03.html b/ch11/ch11-03.html index 5c7e45d..1562180 100644 --- a/ch11/ch11-03.html +++ b/ch11/ch11-03.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-04.html b/ch11/ch11-04.html index 1a91813..00c52e3 100644 --- a/ch11/ch11-04.html +++ b/ch11/ch11-04.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-05.html b/ch11/ch11-05.html index 0ccc905..d182bbd 100644 --- a/ch11/ch11-05.html +++ b/ch11/ch11-05.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11-06.html b/ch11/ch11-06.html index 5b17355..a20b373 100644 --- a/ch11/ch11-06.html +++ b/ch11/ch11-06.html @@ -84,7 +84,7 @@ diff --git a/ch11/ch11.html b/ch11/ch11.html index 343a9f1..6b3f984 100644 --- a/ch11/ch11.html +++ b/ch11/ch11.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-01.html b/ch12/ch12-01.html index 6240ba7..7902663 100644 --- a/ch12/ch12-01.html +++ b/ch12/ch12-01.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-02.html b/ch12/ch12-02.html index 5c760fb..614b639 100644 --- a/ch12/ch12-02.html +++ b/ch12/ch12-02.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-03.html b/ch12/ch12-03.html index 26bbfd5..cd7aadd 100644 --- a/ch12/ch12-03.html +++ b/ch12/ch12-03.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-04.html b/ch12/ch12-04.html index 796b346..88ce86c 100644 --- a/ch12/ch12-04.html +++ b/ch12/ch12-04.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-05.html b/ch12/ch12-05.html index 76b0b66..8085560 100644 --- a/ch12/ch12-05.html +++ b/ch12/ch12-05.html @@ -84,7 +84,7 @@ diff --git a/ch12/ch12-06.html b/ch12/ch12-06.html index 1b567a2..72c5c04 100644 --- a/ch12/ch12-06.html +++ b/ch12/ch12-06.html @@ -84,7 +84,7 @@ @@ -322,7 +322,7 @@ func Unmarshal(data []byte, out interface{}) (err error) { - @@ -336,7 +336,7 @@ func Unmarshal(data []byte, out interface{}) (err error) { - diff --git a/ch12/ch12-07.html b/ch12/ch12-07.html new file mode 100644 index 0000000..8513e57 --- /dev/null +++ b/ch12/ch12-07.html @@ -0,0 +1,354 @@ + + + + + + 获取结构体字段标签 - Go语言圣经 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+
+ + +
+ +

12.7. 获取结构体字段标签

+

在4.5节我们使用构体成员标签用于设置对应JSON对应的名字。其中json成员标签让我们可以选择成员的名字和抑制零值成员的输出。在本节,我们将看到如何通过反射机制类获取成员标签。

+

对于一个web服务,大部分HTTP处理函数要做的第一件事情就是展开请求中的参数到本地变量中。我们定义了一个工具函数,叫params.Unpack,通过使用结构体成员标签机制来让HTTP处理函数解析请求参数更方便。

+

首先,我们看看如何使用它。下面的search函数是一个HTTP请求处理函数。它定义了一个匿名结构体类型的变量,用结构体的每个成员表示HTTP请求的参数。其中结构体成员标签指明了对于请求参数的名字,为了减少URL的长度这些参数名通常都是神秘的缩略词。Unpack将请求参数填充到合适的结构体成员中,这样我们可以方便地通过合适的类型类来访问这些参数。

+

gopl.io/ch12/search

+
import "gopl.io/ch12/params"
+
+// search implements the /search URL endpoint.
+func search(resp http.ResponseWriter, req *http.Request) {
+	var data struct {
+		Labels     []string `http:"l"`
+		MaxResults int      `http:"max"`
+		Exact      bool     `http:"x"`
+	}
+	data.MaxResults = 10 // set default
+	if err := params.Unpack(req, &data); err != nil {
+		http.Error(resp, err.Error(), http.StatusBadRequest) // 400
+		return
+	}
+
+	// ...rest of handler...
+	fmt.Fprintf(resp, "Search: %+v\n", data)
+}
+
+

下面的Unpack函数主要完成三件事情。第一,它调用req.ParseForm()来解析HTTP请求。然后,req.Form将包含所有的请求参数,不管HTTP客户端使用的是GET还是POST请求方法。

+

下一步,Unpack函数将构建每个结构体成员有效参数名字到成员变量的映射。如果结构体成员有成员标签的话,有效参数名字可能和实际的成员名字不相同。reflect.Type的Field方法将返回一个reflect.StructField,里面含有每个成员的名字、类型和可选的成员标签等信息。其中成员标签信息对应reflect.StructTag类型的字符串,并且提供了Get方法用于解析和根据特定key提取的子串,例如这里的http:"..."形式的子串。

+

gopl.io/ch12/params

+
// Unpack populates the fields of the struct pointed to by ptr
+// from the HTTP request parameters in req.
+func Unpack(req *http.Request, ptr interface{}) error {
+	if err := req.ParseForm(); err != nil {
+		return err
+	}
+
+	// Build map of fields keyed by effective name.
+	fields := make(map[string]reflect.Value)
+	v := reflect.ValueOf(ptr).Elem() // the struct variable
+	for i := 0; i < v.NumField(); i++ {
+		fieldInfo := v.Type().Field(i) // a reflect.StructField
+		tag := fieldInfo.Tag           // a reflect.StructTag
+		name := tag.Get("http")
+		if name == "" {
+			name = strings.ToLower(fieldInfo.Name)
+		}
+		fields[name] = v.Field(i)
+	}
+
+	// Update struct field for each parameter in the request.
+	for name, values := range req.Form {
+		f := fields[name]
+		if !f.IsValid() {
+			continue // ignore unrecognized HTTP parameters
+		}
+		for _, value := range values {
+			if f.Kind() == reflect.Slice {
+				elem := reflect.New(f.Type().Elem()).Elem()
+				if err := populate(elem, value); err != nil {
+					return fmt.Errorf("%s: %v", name, err)
+				}
+				f.Set(reflect.Append(f, elem))
+			} else {
+				if err := populate(f, value); err != nil {
+					return fmt.Errorf("%s: %v", name, err)
+				}
+			}
+		}
+	}
+	return nil
+}
+
+

最后,Unpack遍历HTTP请求的name/valu参数键值对,并且根据更新相应的结构体成员。回想一下,同一个名字的参数可能出现多次。如果发生这种情况,并且对应的结构体成员是一个slice,那么就将所有的参数添加到slice中。其它情况,对应的成员值将被覆盖,只有最后一次出现的参数值才是起作用的。

+

populate函数小心用请求的字符串类型参数值来填充单一的成员v(或者是slice类型成员中的单一的元素)。目前,它仅支持字符串、有符号整数和布尔型。其中其它的类型将留做练习任务。

+
func populate(v reflect.Value, value string) error {
+	switch v.Kind() {
+	case reflect.String:
+		v.SetString(value)
+
+	case reflect.Int:
+		i, err := strconv.ParseInt(value, 10, 64)
+		if err != nil {
+			return err
+		}
+		v.SetInt(i)
+
+	case reflect.Bool:
+		b, err := strconv.ParseBool(value)
+		if err != nil {
+			return err
+		}
+		v.SetBool(b)
+
+	default:
+		return fmt.Errorf("unsupported kind %s", v.Type())
+	}
+	return nil
+}
+
+

如果我们上上面的处理程序添加到一个web服务器,则可以产生以下的会话:

+
$ 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
+
+

练习 12.11: 编写相应的Pack函数,给定一个结构体值,Pack函数将返回合并了所有结构体成员和值的URL。

+

练习 12.12: 扩展成员标签以表示一个请求参数的有效值规则。例如,一个字符串可以是有效的email地址或一个信用卡号码,还有一个整数可能需要是有效的邮政编码。修改Unpack函数以检查这些规则。

+

练习 12.13: 修改S表达式的编码器(§12.4)和解码器(§12.6),采用和encoding/json包(§4.5)类似的方式使用成员标签中的sexpr:"..."字串。

+ + + +
+ + + + + +
+ + + +
+ +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ch12/ch12-08.html b/ch12/ch12-08.html index 0408a25..4e32fe0 100644 --- a/ch12/ch12-08.html +++ b/ch12/ch12-08.html @@ -84,7 +84,7 @@ @@ -217,7 +217,7 @@ methods.Print(new(strings.Replacer))