Go语言编程笔记--web网站开发

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())
	}
}

Buy me a 肥仔水!