1 net/http
包
用于提供Web服务,响应并处理客户端(浏览器)的HTTP请求
使用io包
而不是fmt包来输出字符串,这样源文件编译成可执行文件后,体积要小很多
,运行起来也更省资源。
hello world
package main
import (
"io"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, world!")
}
func main() {
http.HandleFunc("/hello", helloHandler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}
1) helloHandler() 方法是 http.HandlerFunc 类型的实例,并传入 http.ResponseWriter和http.Request作为其必要的两个参数
http.ResponseWriter 类型的对象用于包装处理HTTP服务端的响应信息
r *http.Request表示的是此次HTTP请求的一个数据结构体,即代表一个客户端
2) http.ListenAndServe(),用于在示例中监听 8080 端口,接受并调用内部程序来处理连接到此端口的请求
2 实例 – 简单的相册网站
支持图片上传;
在网页中可以查看已上传的图片;
能看到所有上传的图片列表;
可以删除指定的图片
0 项目结构
├── photoweb.go
├── public
├── css
├── images
└── js
├── uploads
└── views
├── list.html
└── upload.html
1 使用net/http
包提供网络服务
上传图片
在网页上显示图片
处理不存在的图片访问
列出所有已上传图片
2 使用 html/template包
渲染网页模板
3 模板缓存
对模板进行缓存,即指一次性预加载模板。我们可以在photoweb程序初始化运行的时候,将 所有模板一次性加载到程序中。
正好Go的包加载机制
允许我们在init()函数
中做这样的事情, init()会在main()函数之前执行
4 动态请求和静态资源分离
net/http
包提供的这个ServeFile()
函数可以将服务端的一个文件内容读写到 http.ResponseWriter并返回给请求来源的 *http.Request客户端
代码实例:
package main
import (
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"runtime/debug"
)
const (
ListDir = 0x0001
UPLOAD_DIR = "./uploads"
TEMPLATE_DIR = "./views"
)
templates_ := make(map[string]*template.Template)
func init() {
fileInfoArr, err := ioutil.ReadDir(TEMPLATE_DIR)
check(err)
var templateName, templatePath string
for _, fileInfo := range fileInfoArr {
templateName = fileInfo.Name()
if ext := path.Ext(templateName); ext != ".html" {
continue
}
templatePath = TEMPLATE_DIR + "/" + templateName
log.Println("Loading template:", templatePath)
t := template.Must(template.ParseFiles(templatePath))
templates_[templateName] = t
}
}
func check(err error) {
if err != nil {
panic(err)
}
}
func renderHtml(w http.ResponseWriter, tmpl string, locals map[string]interface{}) {
err := templates_[tmpl].Execute(w, locals)
check(err)
}
func isExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
return os.IsExist(err)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
renderHtml(w, "upload", nil);
}
if r.Method == "POST" {
f, h, err := r.FormFile("image")
check(err)
filename := h.Filename
defer f.Close()
t, err := ioutil.TempFile(UPLOAD_DIR, filename)
check(err)
defer t.Close()
_, err := io.Copy(t, f)
check(err)
http.Redirect(w, r, "/view?id="+filename,
http.StatusFound)
}
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
imageId = r.FormValue("id")
imagePath = UPLOAD_DIR + "/" + imageId
if exists := isExists(imagePath);!exists {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "image")
http.ServeFile(w, r, imagePath)
}
func listHandler(w http.ResponseWriter, r *http.Request) {
fileInfoArr, err := ioutil.ReadDir("./uploads")
check(err)
locals := make(map[string]interface{})
images := []string{}
for _, fileInfo := range fileInfoArr {
images = append(images, fileInfo.Name)
}
locals["images"] = images
renderHtml(w, "list", locals)
}
func safeHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if e, ok := recover().(error); ok {
http.Error(w, err.Error(), http.StatusInternalServerError)
// 或者输出自定义的50x错误页面
// w.WriteHeader(http.StatusInternalServerError)
// renderHtml(w, "error", e)
// logging
log.Println("WARN: panic in %v. - %v", fn, e)
log.Println(string(debug.Stack()))
}
}()
fn(w, r)
}
}
func staticDirHandler(mux *http.ServeMux, prefix string, staticDir string, flags int)(mux.HandleFunc(prefix, func(w http.ResponseWriter, *http.Request) {
file := staticDir + r.URL.Path[len(prefix)-1:]
if (flags & ListDir) == 0 {
if exists := isExists(file); !exists {
http.NotFound(w, r)
return
}
}
http.ServeFile(w, r, file)
})
}
func main() {
mux := http.NewServeMux()
staticDirHandler(mux, "/assets/", "./public", 0)
mux.HandleFunc("/", safeHandler(listHandler))
mux.HandleFunc("/view", safeHandler(viewHandler))
mux.HandleFunc("/upload", safeHandler(uploadHandler))
err := http.ListenAndServe(":8080", mux)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}