返回首页 - Notes - 2017

Go Web 开发


最简单的 Web 处理程序

简单路由处理

package main

import (
  "fmt"
  "net/http"
  "strings"
  "time"
)

const Port = ":8080"

// 处理动态内容
func serveDynamic(w http.ResponseWriter, r *http.Request) {
  response := "现在时间:" + time.Now().Format("2006-01-02 15:04:05")
  fmt.Fprintln(w, response)
}

// 处理静态内容
func serveStatic(w http.ResponseWriter, r *http.Request) {
  name := strings.TrimLeft(r.RequestURI, "/")
  http.ServeFile(w, r, name+".html")
}

func main() {
  http.HandleFunc("/", serveDynamic)
  http.HandleFunc("/hello", serveStatic)
  http.ListenAndServe(Port, nil)
}

简易文件浏览器

package main

import "net/http"

func main() {
  http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))
}

路由

使用 gorilla/mux 第三方路由组件

package main

import (
  "net/http"
  "os"

  "github.com/gorilla/mux"
)

const Port = ":8080"

func pageHandler(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  fileName := "files/" + vars["id"] + ".html"
  if _, err := os.Stat(fileName); err != nil {
    fileName = "files/404.html"
  }
  http.ServeFile(w, r, fileName)
}

func main() {
  routes := mux.NewRouter()
  routes.HandleFunc("/page/{id:[0-9]+}", pageHandler)

  http.Handle("/", routes)
  http.ListenAndServe(Port, nil)
}

路由跳转:

func RedirIndex(w http.ResponseWriter, r *http.Request) {
  http.Redirect(w, r, "/home", 301)
}

连接数据库

连接 MySQL 数据库

数据表初始化:

CREATE TABLE pages (
  id int(11) unsigned NOT NULL AUTO_INCREMENT,
  title varchar(256) DEFAULT NULL,
  content mediumtext,
  date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;


INSERT INTO pages (title, content) VALUES ('Hello World', 'Hello World Content');

服务端代码:

package main

import (
  "database/sql"
  "fmt"
  "log"
  "net/http"

  _ "github.com/go-sql-driver/mysql"
  "github.com/gorilla/mux"
)

const (
  DBHost = "127.0.0.1" // 数据库IP地址
  DBPort = ":3306"     // 数据库端口号
  DBUser = "root"      // 数据库用户名
  DBPass = "root"      // 数据库密码
  DBBase = "hello"     // 数据库名
  Port   = ":8080"     // 本地服务端口号
)

// 数据库连接句柄
var database *sql.DB

// 数据表结构
type Page struct {
  Title   string
  Content string
  Date    string
}

func ServePage(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  item := Page{}
  err := database.QueryRow("SELECT title,content,date FROM pages WHERE id = ?", vars["id"]).Scan(&item.Title, &item.Content, &item.Date)
  if err != nil {
    log.Println(err)
    http.Error(w, http.StatusText(404), http.StatusNotFound)
    return
  }
  html := `<html><head><title>` + item.Title + `</title></head><body><h1>` + item.Title + `</h1><div>` + item.Content + `</div></body></html>`
  fmt.Fprintln(w, html)
}

func main() {
  dbConn := fmt.Sprintf("%s:%s@tcp(%s%s)/%s", DBUser, DBPass, DBHost, DBPort, DBBase)
  db, err := sql.Open("mysql", dbConn)
  if err != nil {
    log.Println(err.Error)
    return
  }
  database = db

  routes := mux.NewRouter()
  routes.HandleFunc("/page/{id:[0-9]+}", ServePage)

  http.Handle("/", routes)
  http.ListenAndServe(Port, nil)
}

一次查询多条记录:

  var pages = []Page{}
  data, err := database.Query("SELECT title,content,date FROM pages ORDER BY ? DESC", "date")
  defer data.Close()
  if err != nil {
    return
  }

  for data.Next() {
    item := Page{}
    data.Scan(&item.Title, &item.RawContent, &item.Date)
    item.Content = template.HTML(item.RawContent)
    pages = append(pages, item)
  }

使用模版

模版文件的内容:

<!DOCTYPE HTML>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <title>{{.Title}}</title>
  </head>
  <body>
    <h1>{{.Title}}</h1>
    <p>
      {{.Content}}
    </p>
    <div>{{.Date}}</div>
  </body>
</html>

服务端代码:

import "html/template"

func ServePage(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  item := Page{}
  err := database.QueryRow("SELECT title,content,date FROM pages WHERE id = ?", vars["id"]).Scan(&item.Title, &item.Content, &item.Date)
  if err != nil {
    log.Println(err)
    http.Error(w, http.StatusText(404), http.StatusNotFound)
    return
  }

  t, _ := template.ParseFiles("templates/page.html")
  t.Execute(w, item)
}

使用安全的 HTML

// 数据表结构
type Page struct {
  Title      string
  RawContent string
  Content    template.HTML
  Date       string
}

func ServePage(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  item := Page{}
  err := database.QueryRow("SELECT title,content,date FROM pages WHERE id = ?", vars["id"]).Scan(&item.Title, &item.RawContent, &item.Date)
  if err != nil {
    log.Println(err)
    http.Error(w, http.StatusText(404), http.StatusNotFound)
    return
  }

  item.Content = template.HTML(item.RawContent)
  t, _ := template.ParseFiles("templates/page.html")
  t.Execute(w, item)
}

返回 JSON

itemJson, err := json.Marshal(item)
if err != nil {
  http.Error(w, err.Error(), http.StatusInternalServerError)
  return
}
w.Header().Set("Content-Type", "application/json;charset=utf-8")
fmt.Fprintln(w, string(itemJson))

date:2017-06-28、2017-06-29、2017-06-30