一.文件上传
  • 文件上传:客户端把上传文件转换为二进制流后发送给服务器,服务器对二进制流进行解析

  • HTML表单(form)enctype(Encode Type)属性控制表单在提交数据到服务器时数据的编码类型.

    • enctype=”application/x-www-form-urlencoded” 默认值,表单数据会被编码为名称/值形式

    • enctype=”multipart/form-data” 编码成消息,每个控件对应消息的一部分.请求方式必须是post

    • enctype=”text/plain” 纯文本形式进行编码的

  • HTML模版内容如下(在项目/view/index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<form action="upload" enctype="multipart/form-data" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="file" name="photo"/><br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>
  • 服务端可以使用FormFIle("name")获取上传到的文件,官方定义如下

// FormFile returns the first file for the provided form key.
// FormFile calls ParseMultipartForm and ParseForm if necessary.
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
    if r.MultipartForm == multipartByReader {
        return nil, nil, errors.New("http: multipart handled by MultipartReader")
    }
    if r.MultipartForm == nil {
        err := r.ParseMultipartForm(defaultMaxMemory)
        if err != nil {
            return nil, nil, err
        }
    }
    if r.MultipartForm != nil && r.MultipartForm.File != nil {
        if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
            f, err := fhs[0].Open()
            return f, fhs[0], err
        }
    }
    return nil, nil, ErrMissingFile
}
  • multipart.File是文件对象

// File is an interface to access the file part of a multipart message.
// Its contents may be either stored in memory or on disk.
// If stored on disk, the File's underlying concrete type will be an *os.File.
type File interface {
    io.Reader
    io.ReaderAt
    io.Seeker
    io.Closer
}
  • 封装了文件的基本信息

// A FileHeader describes a file part of a multipart request.
type FileHeader struct {
    Filename string                 //文件名
    Header   textproto.MIMEHeader   //MIME信息
    Size     int64                  //文件大小,单位bit
​
    content []byte                  //文件内容,类型[]byte
    tmpfile string                  //临时文件
}
  • 服务器端编写代码如下

    • 获取客户端传递后的文件流,把文件保存到服务器即可

package main
​
import (
   "net/http"
   "fmt"
   "html/template"
   "io/ioutil"
)
​
/*
显示欢迎页upload.html
 */
func welcome(rw http.ResponseWriter, r *http.Request) {
   t, _ := template.ParseFiles("template/html/upload.html")
   t.Execute(rw, nil)
}
​
/*
文件上传
 */
func upload(rw http.ResponseWriter, r *http.Request) {
   //获取普通表单数据
   username := r.FormValue("username")
   fmt.Println(username)
   //获取文件流,第三个返回值是错误对象
   file, header, _ := r.FormFile("photo")
   //读取文件流为[]byte
   b, _ := ioutil.ReadAll(file)
   //把文件保存到指定位置
   ioutil.WriteFile("D:/new.png", b, 0777)
   //输出上传时文件名
   fmt.Println("上传文件名:", header.Filename)
}
​
func main() {
   server := http.Server{Addr: "localhost:8899"}
​
   http.HandleFunc("/", welcome)
   http.HandleFunc("/upload", upload)
​
   server.ListenAndServe()
}
二.文件下载简介
  • 文件下载总体步骤

    • 客户端向服务端发起请求,请求参数包含要下载文件的名称

    • 服务器接收到客户端请求后把文件设置到响应对象中,响应给客户端浏览器

  • 载时需要设置的响应头信息

    • Content-Type: 内容MIME类型

      • application/octet-stream 任意类型

    • Content-Disposition:客户端对内容的操作方式

      • inline 默认值,表示浏览器能解析就解析,不能解析下载

      • attachment;filename=下载时显示的文件名 ,客户端浏览器恒下载

代码
  • 在view/index.html中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="download?filename=abc.png">下载</a>
</body>
</html>
  • 在main.go中编写

package main
​
import (
   "net/http"
   "html/template"
   "io/ioutil"
)
​
func showDownloadPage(rw http.ResponseWriter,r *http.Request){
   t,_:=template.ParseFiles("template/html/download.html")
   t.Execute(rw,nil)
}
func download(rw http.ResponseWriter,r *http.Request){
   //获取请求参数
   fn:=r.FormValue("filename")
   //设置响应头
   header:=rw.Header()
   header.Add("Content-Type","application/octet-stream")
   header.Add("Content-Disposition","attachment;filename="+fn)
   //使用ioutil包读取文件
   b,_:=ioutil.ReadFile("D:/"+fn)
   //写入到响应流中
   rw.Write(b)
}
​
func main() {
   server:=http.Server{Addr:"localhost:8899"}
​
   http.HandleFunc("/showdownload",showDownloadPage)
   http.HandleFunc("/download",download)
​
   server.ListenAndServe()
}

 

三.JSON简介
{"key":value,"key":value}
[{"key":"value"},{"key":"value"}]
// 字段被本包忽略
Field int `json:"-"`
// 字段在json里的键为"myName"
Field int `json:"myName"`
// 字段在json里的键为"myName"且如果字段为空值将在对象中省略掉
Field int `json:"myName,omitempty"`
// 字段在json里的键为"Field"(默认值),但如果字段为空值会跳过;注意前导的逗号
Field int `json:",omitempty"`

 

代码示例
  • 结构体和[]byte进行转换代码比较简单

    • 只要满足键值对形式的类型都可以转换成标准的json格式

package main
​
import (
    "encoding/json"
    "fmt"
)
​
type User struct {
    Name string
    Age  int
}
​
func main() {
    user := User{"张三", 12}
    //user:=map[string]interface{}{"Name":"张三","Age":18}
    //把结构体转换为[]byte
    b, _ := json.Marshal(user)
    fmt.Println(string(b))
    //把[]byte转为json
    u2 := new(User)
    json.Unmarshal(b, u2)
    fmt.Println(u2)
}

 

四.Ajax访问返回json数据
  • 使用jQuery封装的$.post()进行ajax请求

  • HTML页面发送ajax请求,请求数据

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="/static/js/jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () {
            $("button").click(function () {
                $.post("getUser", function (data) {
                    var result = "";
                    for (var i = 0; i < data.length; i++) {
                        result += "<tr>";
                        result += "<td>";
                        result += data[i].Name;
                        result += "</td>";
                        result += "<td>";
                        result += data[i].Age;
                        result += "</td>";
                        result += "</tr>";
                    }
                    $("#t_tbody").html(result)
                })
            })
        })
    </script>
</head>
<body>
<button>加载数据到表格</button>
<table border="1">
    <tr>
        <th>姓名</th>
        <th>年龄</th>
    </tr>
    <tbody id="t_tbody">
​
    </tbody>
</table>
</body>
</html>
  • 服务端返回json数据即可.

package main
​
import (
    "net/http"
    "html/template"
    "encoding/json"
    "fmt"
)
​
type User struct {
    Name string
    Age  int
}
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
func getUser(w http.ResponseWriter, r *http.Request) {
    users := make([]User, 0)
    users = append(users, User{"张三", 12})
    users = append(users, User{"李四", 13})
    users = append(users, User{"王五", 14})
    w.Header().Set("Content-type", "application/json;charset=utf-8")
    b, _ := json.Marshal(users)
    fmt.Fprintln(w, string(b))
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)        //首页
    http.HandleFunc("/getUser", getUser) //获取信息Handler
    server.ListenAndServe()
}
五. 正则表达式
  • 正则表达式:(Regular Expression)

  • 正则表达式就正则字符和普通字符组成字符串的规则

  • 正则内容如下

单字符:
        .              任意字符(标志s==true时还包括换行符)
        [xyz]          字符族
        [^xyz]         反向字符族
        \d             Perl预定义字符族
        \D             反向Perl预定义字符族
        [:alpha:]      ASCII字符族
        [:^alpha:]     反向ASCII字符族
        \pN            Unicode字符族(单字符名),参见unicode包
        \PN            反向Unicode字符族(单字符名)
        \p{Greek}      Unicode字符族(完整字符名)
        \P{Greek}      反向Unicode字符族(完整字符名)
结合:
        xy             匹配x后接着匹配y
        x|y            匹配x或y(优先匹配x)
重复:
        x*             重复>=0次匹配x,越多越好(优先重复匹配x)
        x+             重复>=1次匹配x,越多越好(优先重复匹配x)
        x?             0或1次匹配x,优先1次
        x{n,m}         n到m次匹配x,越多越好(优先重复匹配x)
        x{n,}          重复>=n次匹配x,越多越好(优先重复匹配x)
        x{n}           重复n次匹配x
        x*?            重复>=0次匹配x,越少越好(优先跳出重复)
        x+?            重复>=1次匹配x,越少越好(优先跳出重复)
        x??            0或1次匹配x,优先0次
        x{n,m}?        n到m次匹配x,越少越好(优先跳出重复)
        x{n,}?         重复>=n次匹配x,越少越好(优先跳出重复)
        x{n}?          重复n次匹配x
分组:
        (re)           编号的捕获分组
        (?P<name>re)   命名并编号的捕获分组
        (?:re)         不捕获的分组
        (?flags)       设置当前所在分组的标志,不捕获也不匹配
        (?flags:re)    设置re段的标志,不捕获的分组
标志的语法为xyz(设置)、-xyz(清楚)、xy-z(设置xy,清楚z),标志如下:
        I              大小写敏感(默认关闭)
        m              ^和$在匹配文本开始和结尾之外,还可以匹配行首和行尾(默认开启)
        s              让.可以匹配\n(默认关闭)
        U              非贪婪的:交换x*和x*?、x+和x+?……的含义(默认关闭)
边界匹配:
        ^              匹配文本开始,标志m为真时,还匹配行首
        $              匹配文本结尾,标志m为真时,还匹配行尾
        \A             匹配文本开始
        \b             单词边界(一边字符属于\w,另一边为文首、文尾、行首、行尾或属于\W)
        \B             非单词边界
        \z             匹配文本结尾
转义序列:
        \a             响铃符(\007)
        \f             换纸符(\014)
        \t             水平制表符(\011)
        \n             换行符(\012)
        \r             回车符(\015)
        \v             垂直制表符(\013)
        \123           八进制表示的字符码(最多三个数字)
        \x7F           十六进制表示的字符码(必须两个数字)
        \x{10FFFF}     十六进制表示的字符码
        \*             字面值'*'
        \Q...\E        反斜线后面的字符的字面值
字符族(预定义字符族之外,方括号内部)的语法:
        x              单个字符
        A-Z            字符范围(方括号内部才可以用)
        \d             Perl字符族
        [:foo:]        ASCII字符族
        \pF            单字符名的Unicode字符族
        \p{Foo}        完整字符名的Unicode字符族
预定义字符族作为字符族的元素:
        [\d]           == \d
        [^\d]          == \D
        [\D]           == \D
        [^\D]          == \d
        [[:name:]]     == [:name:]
        [^[:name:]]    == [:^name:]
        [\p{Name}]     == \p{Name}
        [^\p{Name}]    == \P{Name}
Perl字符族:
        \d             == [0-9]
        \D             == [^0-9]
        \s             == [\t\n\f\r ]
        \S             == [^\t\n\f\r ]
        \w             == [0-9A-Za-z_]
        \W             == [^0-9A-Za-z_]
ASCII字符族:
        [:alnum:]      == [0-9A-Za-z]
        [:alpha:]      == [A-Za-z]
        [:ascii:]      == [\x00-\x7F]
        [:blank:]      == [\t ]
        [:cntrl:]      == [\x00-\x1F\x7F]
        [:digit:]      == [0-9]
        [:graph:]      == [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]
        [:lower:]      == [a-z]
        [:print:]      == [ -~] == [ [:graph:]]
        [:punct:]      == [!-/:-@[-`{-~]
        [:space:]      == [\t\n\v\f\r ]
        [:upper:]      == [A-Z]
        [:word:]       == [0-9A-Za-z_]
        [:xdigit:]     == [0-9A-Fa-f]   
  
/+特殊字母 : 代表某个取值范围

[内容]:代表一个字符,字符的取值范围就是内部的内容

{n,m}个数,大于等于n小于等于m个

.一个任意内容的字符

^开始

$结束

+至少一个

*任意个

?最多一个
 Go语言对正则的支持
  • 在regexp包中提供了对正则表达式的支持,并提供了RegExp结构体

    • 可以看出里面有互斥锁,所以在并发下是安全的

// Regexp is the representation of a compiled regular expression.
// A Regexp is safe for concurrent use by multiple goroutines,
// except for configuration methods, such as Longest.
type Regexp struct {
    // read-only after Compile
    regexpRO
​
    // cache of machines for running regexp
    mu      sync.Mutex
    machine []*machine
}
  • 判断字符串是否与正则匹配最简单的办法是

    result,_:=regexp.MatchString(`^\d\w$`,"5A")
    fmt.Println(result)
  • 如果需要更多的功能,可以使用Regexp的方式实现,下面列举除了一些常用方法

package main
​
import (
    "regexp"
    "fmt"
)
​
func main() {
    //创建结构体变量
    r := regexp.MustCompile(`\d[a-zA-Z]`)
    //判断是否匹配
    fmt.Println(r.MatchString("5A1"))
    /*
    字符串中满足要求的片段,返回[]string
    第二个参数是[]string的长度,-1表示不限制长度
     */
    fmt.Println(r.FindAllString("56A6B7C", -1))
    /*
    把正则表达式匹配的结果当作拆分符,拆分字符串
    n > 0 : 返回最多n个子字符串,最后一个子字符串是剩余未进行分割的部分。
    n == 0: 返回nil (zero substrings)
    n < 0 : 返回所有子字符串
     */
    fmt.Println(r.Split("12345qwert", -1))
    //把满足正则要求内容替换成指定字符串
    fmt.Println(r.ReplaceAllString("12345qwert", "替换了"))
}
服务器端数据校验
  • 数据校验可以有客户端数据校验和服务器端数据校验.双重保证是保证程序安全性的有效措施

  • 客户端向服务端发送请求参数,服务器端接收到请求参数后使用正则验证,验证结果通过才能正确执行,例如注册时验证数据格式

  • HTML代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title></title>
</head>
<body>
<form action="register" method="post">
    用户名:<input type="text" name="username"/>用户名必须时6-12位,只能包含字符或数字<br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>
 
  • 服务器代码如下

package main
​
import (
    "net/http"
    "html/template"
    "regexp"
    "fmt"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
​
func register(w http.ResponseWriter, r *http.Request) {
    {
        u := r.FormValue("username")
        r, _ := regexp.MatchString(`^[0-9a-zA-Z]{6,12}$`, u)
        if r {
            fmt.Fprintln(w, "注册成功")
        } else {
            fmt.Fprintln(w, "用户名格式不正确")
        }
    }
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)
    http.HandleFunc("/register", register)
    server.ListenAndServe()
}
六.Cookie 简介
  • Cookie就是客户端存储技术.以键值对的形式存在

  • 在B/S架构中,服务器端产生Cookie响应给客户端,浏览器接收后把Cookie存在在特定的文件夹中,以后每次请求浏览器会把Cookie内容放入到请求中

Go语言对Cookie的支持
type Cookie struct {
    Name  string
    Value string
​
    Path       string    // optional
    Domain     string    // optional
    Expires    time.Time // optional
    RawExpires string    // for reading cookies only
​
    // MaxAge=0 means no 'Max-Age' attribute specified.
    // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
    // MaxAge>0 means Max-Age attribute present and given in seconds
    MaxAge   int
    Secure   bool
    HttpOnly bool
    Raw      string
    Unparsed []string // Raw text of unparsed attribute-value pairs
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title></title>
</head>
<body>
<a href="setCookie">产生Cookie</a>
<a href="getCookie">获取Cookie</a>
<br/>
{{.}}
</body>
</html>
package main
​
import (
    "net/http"
    "html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
​
func setCookie(w http.ResponseWriter, r *http.Request) {
    c := http.Cookie{Name: "mykey", Value: "myvalue"}
    http.SetCookie(w, &c)
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
​
}
func getCookie(w http.ResponseWriter, r *http.Request) {
    //根据key取出Cookie
    //c1,_:=r.Cookie("mykey")
    //取出全部Cookie内容
    cs := r.Cookies()
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, cs)
}
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)
    http.HandleFunc("/setCookie", setCookie)
    http.HandleFunc("/getCookie", getCookie)
    server.ListenAndServe()
}
① HttpOnly
  • HttpOnly:控制Cookie的内容是否可以被JavaScript访问到。通过设置HttpOnly为true时防止XSS攻击防御手段之一

  • 默认HttpOnly为false,表示客户端可以通过js获取

  • 在项目中导入jquery.cookie.js库,使用jquery获取客户端Cookie内容

  • HTML代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery-1.7.2.js"></script>
    <script src="/static/js/jquery.cookie.js"></script>
    <script type="text/javascript">
        $(function () {
            $("button").click(function () {
                var value = $.cookie("mykey")
                alert(value)
            })
        })
    </script>
</head>
<body>
<a href="setCookie">产生Cookie</a>
<button>获取cookie</button>
</body>
</html>
  • 服务端代码如下

package main
​
import (
    "net/http"
    "html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
func setCookie(w http.ResponseWriter, r *http.Request) {
    c := http.Cookie{Name: "mykey", Value: "myvalue", HttpOnly: false}
    http.SetCookie(w, &c)
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
​
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)
    http.HandleFunc("/setCookie", setCookie)
    server.ListenAndServe()
}
 ② Path
  • Path属性设置Cookie的访问范围

  • 默认为”/”表示当前项目下所有都可以访问

  • Path设置路径及子路径内容都可以访问

  • 首先先访问index.html,点击超链接产生cookie,在浏览器地址栏输入localhost:8090/abc/mypath后发现可以访问cookie

  • html代码没有变化,只需要修改服务器端代码如下

package main
​
import (
    "net/http"
    "html/template"
    "fmt"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
func setCookie(w http.ResponseWriter, r *http.Request) {
    //验证httponly
    //c := http.Cookie{Name: "mykey", Value: "myvalue", HttpOnly: false}
    //验证path
    c := http.Cookie{Name: "mykey", Value: "myvalue", Path: "/abc/"}
    http.SetCookie(w, &c)
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
​
//验证path属性是否生效的handler
func mypath(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, r.Cookies())
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)
    http.HandleFunc("/setCookie", setCookie)
    //路径必须以/abc/开头
    http.HandleFunc("/abc/mypath", mypath)
    server.ListenAndServe()
}
③ Expires
  • Cookie默认存活时间是浏览器不关闭,当浏览器关闭后,Cookie失效

  • 可以通过Expires设置具体什么时候过期,Cookie失效. 也可以通过MaxAge设置Cookie多长时间后实现

  • IE6,7,8和很多浏览器不支持MaxAge,建议使用Expires

  • Expires是time.Time类型,所以设置时需要明确设置过期时间

  • 修改服务器端代码如下.只需要修改创建Cookie的代码,其他位置不变

package main
​
import (
    "net/http"
    "html/template"
    "fmt"
    "time"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
func setCookie(w http.ResponseWriter, r *http.Request) {
    //验证httponly
    //c := http.Cookie{Name: "mykey", Value: "myvalue", HttpOnly: false}
    //验证path
    //c := http.Cookie{Name: "mykey", Value: "myvalue", Path: "/abc/"}
    //验证Expires
    c := http.Cookie{Name: "mykey", Value: "myvalue", Expires: time.Date(2018, 1, 1, 1, 1, 1, 0, time.Local)}
    http.SetCookie(w, &c)
    t, _ := template.ParseFiles("view/index.html")
    t.Execute(w, nil)
}
​
//验证path属性是否生效的handler
func mypath(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, r.Cookies())
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.HandleFunc("/", welcome)
    http.HandleFunc("/setCookie", setCookie)
    //路径必须以/abc/开头
    http.HandleFunc("/abc/mypath", mypath)
    server.ListenAndServe()
}
八.Restful风格请求
  • 在目前所学内容中每个请求都需要绑定一个HandlerFunc,而在实际项目中会有很多URL,且可能出现满足特定规律的URL,例如: /sxt/it和/sxt/baizhan都是以/sxt/开头.且如果这两个URL里面代码也差不多时,写两个Func就属于代码冗余了.

  • 可以使用restful风格把满足特定格式url和功能类似的代码提入到一个func中实现代码复用.

Go语言的多路复用器
  • 在http包中提供了ServeMux实现多路复用器,它会对URL进行解析,然后重定向到正确的处理器上

 

  • ServeMux是一个结构体,里面存放了map和读写锁

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool // whether any patterns contain hostnames
}
  • 在Go语言中有提供了ServeMux的对象DefaultServeMux,

var DefaultServeMux = &defaultServeMux
​
var defaultServeMux ServeMux
  • 而平时使用的http.Server不指定Handler属性时默认就是DefaultServeMux

使用第三方实现Restful风格
  • 可以使用命令,从github上下载第三方库,下载后放入到%GOROOT%/src/github.com中

go get github.com/gorilla/mux
  • 使用mux包的Router实现restful风格

package main
​
import (
    "net/http"
    "fmt"
    "github.com/gorilla/mux"
)
​
func hello(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    fmt.Fprintln(w, "dayinle", vars["key"])
}
​
func abc(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "abc")
}
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/hello/{key}", hello)
    r.HandleFunc("/abc", abc)
    http.ListenAndServe(":8090", r)
    //s:=http.Server{Addr:":8090",Handler:r}
    //s.ListenAndServe()
}
​