Golang模板处理中遇到的常见问题解析
Golang模板处理中遇到的常见问题解析 我尝试将多个变量传递到HTML模板中,但无法实现。
模板如下:
<html>
<head>
<title>Current IP Check</title>
</head>
<body>
<div>当前IP地址:{{.injectIP}}</div>
<div>当前位置:{{.injectCity}}</div>
</body>
</html>
Go文件如下:
package main
// 模块要求
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
type Htmltemp struct {
injectIP string
injectCity string
}
// 用于解析JSON并传递给HTML的模板
type Geo struct {
City string `json:"city"`
}
// 主函数,在80端口创建Web服务器
func main() {
http.HandleFunc("/", ReadUserIP)
http.ListenAndServe(":80", nil)
}
func ReadUserIP(w http.ResponseWriter, r *http.Request) {
//检查X-forwarded-for头部
IPAddress := ""
if IPAddress == "" {
//IPAddress = r.Header.Get("x-forwarded-for")
IPAddress = "134.201.250.155, 23:24:3:5" // -----------待删除,测试用硬编码
}
// 从请求中打印远程地址
if IPAddress == "" {
IPAddress = r.RemoteAddr
}
// 遍历字符串获取所需值
ipadd := strings.Split(IPAddress, ",") //---- 我将:改为,
cip1 := ipadd[0]
//t.Execute(w, cip1)
// 通过插入定位的IP创建地理定位API调用的端点
buffer := bytes.Buffer{}
buffer.WriteString("http://api.ipstack.com/")
buffer.WriteString(cip1)
buffer.WriteString("?access_key=2dd2ca2a9f39638d4f61533ecb1b337b&fields=city") //请在该网站注册并获取新密钥,我更改了我的密钥,另外 - 购买付费版本以使用此API获取时区数据
url := buffer.String()
// 进行API调用以接收IP相关数据
response, err := http.Get(url)
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
//fmt.Println(string(responseData)) // 用于查看响应
//JSON解析以及位置和时区插入输出文件"t"待处理
// - 解组JSON
// 然后将它们插入HTML模板中
var geo Geo
err = json.Unmarshal(responseData, &geo)
if err != nil {
panic(err)
}
fmt.Println(geo.City)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t, err := template.ParseFiles("index.html")
if err != nil {
fmt.Fprintf(w, "无法加载模板")
}
inject := Htmltemp{injectIP: cip1, injectCity: geo.City}
t.Execute(w, inject)
}
更多关于Golang模板处理中遇到的常见问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您分享这段经历,这是我用Go语言编写的第一个"Hello World"类型的程序,虽然遇到了挫折但很有意义。现在我已经开始学习教程了。让我惊讶的是,用Go语言完成相对复杂的任务时,代码量看起来非常少。这真是太棒了!
更多关于Golang模板处理中遇到的常见问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
KodeSaga: 导出的变量是否必须以大写字母开头?
是的,以大写字母开头的标识符会从包中导出,小写字母开头的则不会导出,仅在包内可用。
这在教程中甚至在你编写任何有意义的代码之前就已经涵盖了:
你看到了什么错误?如果你没有看到错误,请确保不要忽略 t.Execute(…) 的结果。
PS 并编辑:
我费了些功夫在我的环境中运行了你的代码,并打印了 t.Execute(…) 的结果:
template: index.html:9:33: executing "index.html" at <.injectIP>: injectIP is an unexported field of struct type main.Htmltemp
由于模板是通过反射来渲染的,模板中引用的所有字段都需要从上下文中导出。你不能在模板中使用私有字段。
我已经尝试按照您看到的方式导出它们
type Htmltemp struct {
injectIP string
injectCity string
}
这简直太神奇了,我唯一做的改变就是在代码中将下面字段的首字母改为大写,现在它完美工作了
&lt;div&gt;Current IP Address: {{.InjectIP}}&lt;/div&gt;
&lt;div&gt;Current Location: {{.InjectCity}}&lt;/div&gt;
并且在Go程序中的导出模板中也做了相应更改
type Htmltemp struct {
InjectIP string
InjectCity string
}
这是否意味着导出的变量必须以大写字母开头?
在Go模板处理中传递多个变量时,最常见的问题是结构体字段的可见性。在您的代码中,Htmltemp结构体的字段是小写字母开头,这意味着它们是未导出的(私有字段),模板引擎无法访问这些字段。
以下是修正后的代码:
package main
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
// 修正:将结构体字段改为大写开头,使其可导出
type Htmltemp struct {
InjectIP string
InjectCity string
}
type Geo struct {
City string `json:"city"`
}
func main() {
http.HandleFunc("/", ReadUserIP)
http.ListenAndServe(":80", nil)
}
func ReadUserIP(w http.ResponseWriter, r *http.Request) {
IPAddress := ""
if IPAddress == "" {
IPAddress = "134.201.250.155, 23:24:3:5"
}
if IPAddress == "" {
IPAddress = r.RemoteAddr
}
ipadd := strings.Split(IPAddress, ",")
cip1 := ipadd[0]
buffer := bytes.Buffer{}
buffer.WriteString("http://api.ipstack.com/")
buffer.WriteString(cip1)
buffer.WriteString("?access_key=2dd2ca2a9f39638d4f61533ecb1b337b&fields=city")
url := buffer.String()
response, err := http.Get(url)
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
var geo Geo
err = json.Unmarshal(responseData, &geo)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t, err := template.ParseFiles("index.html")
if err != nil {
fmt.Fprintf(w, "无法加载模板")
return
}
// 使用导出的字段名
inject := Htmltemp{InjectIP: cip1, InjectCity: geo.City}
err = t.Execute(w, inject)
if err != nil {
fmt.Fprintf(w, "模板执行错误: %v", err)
}
}
对应的HTML模板也需要更新字段名:
<html>
<head>
<title>Current IP Check</title>
</head>
<body>
<div>当前IP地址:{{.InjectIP}}</div>
<div>当前位置:{{.InjectCity}}</div>
</body>
</html>
另一个替代方案是使用map来传递变量:
func ReadUserIP(w http.ResponseWriter, r *http.Request) {
// ... 前面的代码保持不变
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t, err := template.ParseFiles("index.html")
if err != nil {
fmt.Fprintf(w, "无法加载模板")
return
}
// 使用map传递变量
data := map[string]interface{}{
"injectIP": cip1,
"injectCity": geo.City,
}
err = t.Execute(w, data)
if err != nil {
fmt.Fprintf(w, "模板执行错误: %v", err)
}
}
使用map方式时,HTML模板可以保持原样不变。主要问题在于Go的可见性规则:只有首字母大写的字段才能被外部包(包括模板引擎)访问。

