首页 存档 技术 查看内容

Go开发HTTP(上)

2018-3-30 13:00 |来自: 互联网 323 0

摘要: Go 是一门新语言。很多人都是用 Go 来开发 Web 服务。Web 开发很多同学急于求成,直接使用beego,echo或iris等知名框架。对标准库net/http的了解甚少。这里我就主要聊一下标准库net/http开发 Web 服务时的使用细节。 ...

Go 是一门新语言。很多人都是用 Go 来开发 Web 服务。Web 开发很多同学急于求成,直接使用beego,echoiris等知名框架。对标准库net/http的了解甚少。这里我就主要聊一下标准库net/http开发 Web 服务时的使用细节。


创建 HTTP 服务

在 Go 中,创建 HTTP 服务很简单:

package main

// in main.go

import (

"fmt"

"net/http"

)

func main(){

if err := http.ListenAndServe(":12345",nil); err != nil{

fmt.Println("start http server fail:",err)

}

}

这样就会启动一个 HTTP 服务在端口12345。浏览器输入http://localhost:12345/就可以访问。当然从代码看出,没有给这个 HTTP 服务添加实际的处理逻辑,所有的访问都是默认的404 Not Found

添加 http.Handler

添加 HTTP 的处理逻辑的方法简单直接:

func main() {

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello, Go HTTP Server"))

})

http.HandleFunc("/abc", func(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello, Go HTTP abc"))

})

if err := http.ListenAndServe(":123456", nil); err != nil {

fmt.Println("start http server fail:", err)

}

}

访问http://localhost:12345/就可以看到页面输出Hello, Go HTTP Server的内容。访问http://localhost:12345/abc就可以看到页面输出Hello, Go HTTP abc的内容。但是 Go 默认的路由匹配机制很弱。上面的代码除了/abc,其他的请求都匹配到/,不足以使用,肯定需要自己写路由的过程。一个简单的方式就是写一个自己的http.Handler

type MyHandler struct{} // 实现 http.Handler 接口的 ServeHTTP 方法

func (mh MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

if r.URL.Path == "/abc" {

w.Write([]byte("abc"))

return

}

if r.URL.Path == "/xyz" {

w.Write([]byte("xyz"))

return

}

w.Write([]byte("index"))

// 这里你可以写自己的路由匹配规则

}

func main() {

http.Handle("/", MyHandler{})

if err := http.ListenAndServe(":12345", nil); err != nil {

fmt.Println("start http server fail:", err)

}

}


这样可以在自己的MyHandler写复杂的路由规则和处理逻辑。http.ListenAndServe的第二个参数写的会更优雅:

func main() {

if err := http.ListenAndServe(":12345", MyHandler{}); err != nil {

fmt.Println("start http server fail:", err)

}

}

http.ServeMux 路由

net/http提供了一个非常简单的路由结构http.ServeMux。方法http.HandleFunc()http.Handler()就是把路由规则和对应函数注册到默认的一个http.ServeMux上。当然,你可以自己创建http.ServeMux来使用:

func handler(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, "Hello, HTTP Server")

}

func main() {

mux := http.NewServeMux()

mux.HandleFunc("/", handler)

http.ListenAndServe(":12345", mux)

}


但是因为http.ServeMux路由规则简单,功能有限,实践都不会用的,如同鸡肋。更推荐使用httprouter

import (

"fmt"

"net/http"

"github.com/julienschmidt/httprouter"

)

// httprouter.Params 是匹配到的路由参数,比如规则 /user/:id 中 的 :id 的对应值

func handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

fmt.Fprint(w, "hello, httprouter")

}

func main() {

router := httprouter.New()

router.GET("/", handle)

if err := http.ListenAndServe(":12345", router); err != nil {

fmt.Println("start http server fail:", err)

}

}


http.Handler/http.HandlerFunc 中间件

Go 的 HTTP 处理过程可以不仅是一个 http.HandlerFunc,而且是一组 http.HandlerFunc,比如:

func handle1(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("handle1"))

}

func handle2(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("handle2"))

}

// 把几个函数组合起来

func makeHandlers(handlers ...http.HandlerFunc) http.HandlerFunc {

return func(w http.ResponseWriter, r *http.Request) {

for _, handler := range handlers {

handler(w, r)

}

}

}

func main() {

http.HandleFunc("/", makeHandlers(handle1, handle2))

if err := http.ListenAndServe(":12345", nil); err != nil {

fmt.Println("start http server fail:", err)

}

}

这种模式开发的框架可以参考 negroni。它的中间件都是以实现 http.Handler 的结构体来组合的。

Request

HTTP 过程的操作主要是针对客户端发来的请求数据在 *http.Request,和返回给客户端的 http.ResponseWriter 两部分。

请求数据 *http.Request 有两个部分:基本数据和传递参数。基本数据比如请求的方法、协议、URL、头信息等可以直接简单获取:

func HttpHandle(w http.ResponseWriter, r *http.Request) {

fmt.Println("Method:", r.Method)

fmt.Println("URL:", r.URL, "URL.Path", r.URL.Path) // 这里是 *net/url.URL 结构,对应的内容可以查API

fmt.Println("RemoteAddress", r.RemoteAddr)

fmt.Println("UserAgent", r.UserAgent())

fmt.Println("Header.Accept", r.Header.Get("Accept"))

fmt.Println("Cookies",r.Cookies())

// 还有很多肯定会有的基本数据,可以查阅 API 找寻一下

}

http.HandleFunc("/", HttpHandle)

if err := http.ListenAndServe(":12345", nil); err != nil {

fmt.Println("start http server fail:", err)

}

表单数据

请求传递的参数,也就是 表单数据,保存在 *http.Request.Form 和 *http.Request.PostForm (POST 或 PUT 的数据),类似 PHP 的 $_REQUEST 和 $_POST/$_PUT 两个部分。

例如 GET /?abc=xyz,获取这个数据并打印到返回内容:

func HttpHandle(w http.ResponseWriter, r *http.Request) {

value := r.FormValue("abc")

w.Write([]byte("GET: abc=" value))

}

访问 http://localhost:12345/?abc=123 就可以看到页面内容 GET: abc=123。POST 的表单数据也是类似:

func HttpHandle(w http.ResponseWriter, r *http.Request) {

v1 := r.FormValue("abc")

v2 := r.PostFormValue("abc")

v3 := r.Form.Get("abc")

v4 := r.PostForm.Get("abc")

fmt.Println(v1 == v2, v1 == v3, v1 == v4)

w.Write([]byte("POST: abc=" v1))

}

注意,这四个值 v1,v2,v3,v4 是相同的值。

如果同一个表单域传递了多个值,需要直接操作 r.Form 或 r.PostForm,比如 GET /?abc=123

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部