web 应用程序 cloudgo-io
源码:Github
实现的功能:
- 支持静态文件服务
- 支持简单的js访问
- 模板输出
- 提交表单,并输出一个表格
- 对
/unknown
给出开发中的提示
运行结果:
/static/
访问静态文件:
显示静态文件中的index.html
页面:
js中访问的url/api/test/
:
html模板的输出:
提交表单后显示表格:
对/unknown
给出开发中的提示:
实现方法:
初始化服务器
给服务器设置Render和Router:
func NewServer() *negroni.Negroni {
formatter := render.New(render.Options {
Directory: "templates",
Extensions: []string{".html"},
IndentJSON: true,
})
n := negroni.Classic()
mx := mux.NewRouter()
initRoutes(mx, formatter)
n.UseHandler(mx)
return n
}
func initRoutes(mx *mux.Router, formatter *render.Render) {
webRoot := os.Getenv("WEBROOT")
if len(webRoot) == 0 {
if root, err := os.Getwd(); err != nil {
panic("Could not retrive working directory")
} else {
webRoot = root
}
}
//各种路由
//static file
mx.PathPrefix("/static").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(webRoot+"/assets/"))))
}
展示静态文件服务
这里使用前缀"/static/"来处理静态文件展示访问,在server.go
中设置路由:
mx.PathPrefix("/static").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(webRoot+"/assets/"))))
http.Dir
将字符串路径转换为文件系统(FileSystem)。
http.FileServer
返回一个使用 FileSystem 接口 root 提供文件访问服务的 HTTP 处理器(fileHandler)。
PathPrefix
可以给路由加上前缀。
StripPrefix
将访问时url中的"/static/“前缀去掉后再交给http.FileServer(http.Dir(webRoot+"/assets/"))
处理,也就能访问到assets目录的路径,如果不使用StripPrefix
而直接使用以下代码,则会出现404 page not found
错误,因为该服务器目录中并没有”/static/"这个路径。
mx.PathPrefix("/static").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
这样一来path中以"/static/"为前缀的url都会被定位到以 webRoot + "/assets/"
为虚拟根目录的文件系统中,从而能够展示assets目录下的各种静态文件。
当assets目录中没有index.html
文件时,直接访问/static/
可以看到如下界面:
当添加了index.html
后,再访问/static/
就能看到相应的html页面了:
这是因为http.FileServer
返回了一个fileHandler
,该结构体实现了ServeHTTP()
方法,而ServeHTTP()
方法中调用了serveFile()
方法,该方法中定义了:
const indexPage = "/index.html"
当指定的目录中存在名为index.html
的文件时,就会将其打开。
js请求支持
首先在服务器中添加路由:
mx.HandleFunc("/api/test", apiTestHandler(formatter)).Methods("GET")
apiTestHandler(*render.Render)
方法中利用Render.JSON
将数据编组为JSON进行响应,以供JS文件进行访问并获取数据:
func apiTestHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.JSON(w, http.StatusOK, struct {
Name string `json:"name"`
Date string `json:"date"`
}{Name: "T-Machine", Date: time.Now().Format("2006-01-02 15:04")})
}
}
这里返回了一个包含两个字符串的JSON,如果直接在浏览器访问/api/test
,则会在页面上显示该JSON:
在assets/js/
中新建一个JS文件,利用ajax
来访问该url从而得到响应的数据。然后在assets中的index.html
中引用该JS,将数据内容显示到页面中对应的元素上:
$(document).ready(function() {
$.ajax({
url: "/api/test"
}).then(function(data) {
$('.name').append(data.name);
$('.date').append(data.date);
});
});
模板输出
编写index.html
,放到templates
目录中,当该模板需要引入静态文件资源时,注意该路径需要于之前设定的访问静态文件的url相同,即:
<img src="static/images/logo.png" height="100%" width="100%"/>
而不是相对于该html文件的相对路径:
<img src="../assets/images/logo.png" height="100%" width="100%"/>
在服务器构建的formatter
中添加模板的路径和模板文件的扩展名:
formatter := render.New(render.Options {
Directory: "templates",
Extensions: []string{".html"},
IndentJSON: true,
})
添加路由:
mx.HandleFunc("/", homeHandler(formatter)).Methods("GET")
在homeHandler(*render.Render)
中利用Render.HTML
给模板填充内容:
func homeHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.HTML(w, http.StatusOK, "index", struct {
Name string `json:"name"`
Day string `json:"day"`
}{Name: "T-Machine", Day: "Friday"})
}
}
设定的这两个字符串将被绑定到模板中的{{.Name}}
和{{.Day}}
上。
表单处理
在templates
中的index.html
中添加表单元素,这里设置了用户名和密码两个内容。
添加路由:
mx.HandleFunc("/", Submit).Methods("POST")
在Submit(w http.ResponseWriter, r *http.Request)
中,首先用Request.ParseForm()
解析请求中的表单,然后从表单中获取相应的值,进行一些必要的逻辑判断,比如表单内容是否为空。然后利用模板渲染一个新的html页面,并将读取出的表单内容填充的其中。
这里采用另一种方法输出模板,即利用text/template
包。使用ParseFiles()
方法来加载模板,然后调用模板对象的Excute()
方法来执行该模板并填充数据。
func Submit(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println(r.Form)
username := r.Form["username"][0]
password := r.Form["password"][0]
if len(username) == 0 || len(password) == 0 {
log.Fatal("err")
return
}
t := template.Must(template.ParseFiles("templates/table.html"))
data := map[string]string{
"Name": username,
"Pass": password,
}
if err := t.Execute(w, data); err != nil {
log.Fatal(err)
}
}
访问unknown
添加路由:
mx.HandleFunc("/unknown", NotImplemented)
然后在NotImplemented
方法中调用http.Error()
输出错误信息即可。
func NotImplemented(w http.ResponseWriter, r *http.Request) {
http.Error(w, "501 Not Implemented", 501)
}