第 9 章:使用 Go 构建 Web 应用程序
Web 应用本质上是服务的一种,合理设计与云原生架构结合,可实现高效、可维护的现代网站。本章将系统介绍 Go 构建 Web 应用的核心方法,包括静态资源、API、模板与表单处理等关键技术。
引言
Web 应用程序是与浏览器交互的服务,需以 HTML 响应资源。虽然 Web 应用常被误解为复杂难维护,但在 Go 语言中,构建网站与微服务共享同样的技术栈与理念,简洁高效。
本章将介绍:
- 用 Go 创建功能齐全的网站
- 静态文件与 asset 管理
- 为 JavaScript 客户端提供 RESTful 端点
- 服务端模板渲染
- HTML 表单处理
- 使用 Wercker 云端构建与部署
处理静态文件和资源
每个 Web 应用都需向浏览器提供静态文件,如 HTML、CSS、JavaScript、图片等。Go 可直接复用微服务的 main.go 和 server.go,实现静态文件服务。
建议目录结构如下:
static-content/
├── js/
├── images/
├── css/
└── index.html
服务端代码示例:
// filepath: server.go
package main
import (
"net/http"
"os"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
"github.com/unrolled/render"
)
func NewServer() *negroni.Negroni {
n := negroni.Classic()
mx := mux.NewRouter()
initRoutes(mx, nil)
n.UseHandler(mx)
return n
}
func initRoutes(mx *mux.Router, formatter *render.Render) {
webRoot := os.Getenv("WEBROOT")
if len(webRoot) == 0 {
root, err := os.Getwd()
if err != nil {
panic("Could not retrieve working directory")
}
webRoot = root
}
mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}
通过上述代码,assets 目录下所有静态文件均可被访问。可先运行服务,验证目录浏览与文件访问功能。
支持 JavaScript 客户端与 RESTful API
现代网站普遍使用 JavaScript 与 RESTful API。Go 可轻松为前端提供 API,结合静态资源实现动态交互。
HTML 示例:
<!-- filepath: assets/index.html -->
<html>
<head>
<link rel="stylesheet" href="css/main.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="js/hello.js"></script>
</head>
<body>
Sample Go Application! <br/>
<div>
<p class="greeting-id">The ID is </p>
<p class="greeting-content">The content is </p>
</div>
</body>
</html>
JavaScript 示例:
// filepath: assets/js/hello.js
$(document).ready(function() {
$.ajax({
url: "/api/test"
}).then(function(data) {
$('.greeting-id').append(data.id);
$('.greeting-content').append(data.content);
});
});
服务端 API 示例:
// filepath: handlers.go
package main
import (
"net/http"
"github.com/unrolled/render"
)
type sampleContent struct {
ID string `json:"id"`
Content string `json:"content"`
}
func testHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
formatter.JSON(w, http.StatusOK, sampleContent{ID: "8675309", Content: "Hello from Go!"})
}
}
在 server.go 的 initRoutes 中添加:
mx.HandleFunc("/api/test", testHandler(formatter)).Methods("GET")
通过上述方式,前端可通过 Ajax 调用 RESTful API,实现数据动态渲染。
使用服务端模板渲染
部分场景需在服务端渲染 HTML,Go 标准库支持高效模板处理。示例:
// filepath: server.go
package main
import (
"net/http"
"text/template"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
)
type sampleContent struct {
ID string
Content string
}
var t *template.Template
func init() {
t = template.Must(template.ParseFiles("assets/templates/index.html"))
}
func homeHandler(w http.ResponseWriter, req *http.Request) {
data := sampleContent{ID: "8675309", Content: "Hello from Go!"}
t.Execute(w, data)
}
func initRoutes(mx *mux.Router) {
mx.PathPrefix("/images/").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir("./assets/images/"))))
mx.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("./assets/css/"))))
mx.HandleFunc("/", homeHandler)
}
模板文件示例:
<!-- filepath: assets/templates/index.html -->
<html>
<head>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<img src="/images/cng.png" height="48" width="48" />
Sample Go Web Application!!
<div>
<p class="greeting-id">The ID is {{ .ID }}</p>
<p class="greeting-content">The content is {{ .Content }}</p>
</div>
</body>
</html>
通过服务端模板,可在响应前动态插入数据,适用于需要服务端渲染的场景。
处理 HTML 表单
Go 处理表单极为简便,表单数据通过 request.Form 以 map[string][]string
形式暴露。示例:
func processFormHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
for k, v := range r.Form {
fmt.Printf("Key %s, Val %s", k, strings.Join(v, ""))
}
fmt.Fprintf(w, "Form handled.\n")
}
注意:需先调用 ParseForm 或 ParseMultipartForm 才能访问 Form 属性。
使用 Cookie 和会话状态
Go 标准库 net/http 原生支持 Cookie 操作。写入 Cookie 示例:
func cookieWriteHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
expiration := time.Now().Add(2 * 24 * time.Hour)
cookie := http.Cookie{Name: "sample", Value: "this is a cookie", Expires: expiration}
http.SetCookie(w, &cookie)
formatter.JSON(w, http.StatusOK, "cookie set")
}
}
读取 Cookie 示例:
func cookieReadHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
cookie, err := req.Cookie("sample")
if err == nil {
fmt.Fprint(w, cookie.Value)
} else {
fmt.Fprintf(w, "failed to read cookie, %v", err)
}
}
}
路由配置:
mx.HandleFunc("/cookies/write", cookieWriteHandler(formatter)).Methods("GET")
mx.HandleFunc("/cookies/read", cookieReadHandler(formatter)).Methods("GET")
建议仅在 Cookie 中存储非敏感信息,避免安全风险。
使用 Wercker 构建和部署
持续集成与自动化部署是现代 Web 应用开发的关键。Wercker 可自动构建、测试并生成 Docker 镜像,便于云端部署。
Wercker 构建 pipeline 示例:
build:
steps:
- setup-go-workspace
- script:
name: go get
code: |
cd $WERCKER_SOURCE_DIR
go version
go get -u github.com/Masterminds/glide
go get -u github.com/cloudnativego/cf-tools/vcapinate
export PATH=$WERCKER_SOURCE_DIR/bin:$PATH
glide install
- script:
name: go build
code: |
go build
- script:
name: go test
code: |
go test -v $(glide novendor)
- script:
name: copy files to wercker output
code: |
cp -R ./ ${WERCKER_OUTPUT_DIR}
构建完成后,可通过 Docker 运行:
docker run -p 8100:8100 cloudnativego/web-application:latest
# [negroni] listening on :8100
总结
本章系统介绍了使用 Go 构建 Web 应用的核心方法,包括静态资源管理、API 与前端集成、服务端模板、表单与 Cookie 处理,以及 Wercker 持续集成与部署。通过实践这些模式,开发者可高效构建现代云原生 Web 应用。建议结合实际项目,深入理解 Go Web 开发与自动化部署,为后续复杂系统开发打下坚实基础。
参考文献
- Font Awesome - fortawesome.github.io
- Go 官方文档 - golang.org
- Negroni 中间件库 - github.com
- Gorilla Mux 路由库 - github.com
- Unrolled Render - github.com
- Wercker 服务文档 - devcenter.wercker.com