Go网络编程之HTTP编程

     HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,定义了客户端和服务端之间请求和响应的传输标准。

    Go语言标准库内建提供了net/http包,涵盖了HTTP客户端和服务端的具体实现。使用net/http包,我们可以很方便地编写HTTP客户端或服务端的程序。

 

1.HTTP客户端

   net/http包的Client类提供了如下几个方法,让我们可以用最简洁的方式实现HTTP请求:

      下面概要介绍这几个方法。

         ·http.Get()

          要请求一个资源,只需调用http.Get()方法(等价于http.DefaultClient.Get())即可,示例代码如下:

 

   上面这段代码请求一个网站首页,并将其网页内容打印到标准输出流中。

   ·http.Post()

   要以POST的方式发送数据,也很简单,只需要调用http.Post()方法并依次传递下面的3个参数即可:

   1.请求的目标URL

   2.将要POST数据的资源类型(MIMEType)

   3.数据的比特流([]byte形式)

   下面的示例代码演示了如何上传一张图片:

      ·http.PostForm()

       http.PostForm()方法实现了标准编码格式为application/x-www-form-urlencoded的表单提交。下面的示例代码模拟HTML表单提交一篇新文章:

       ·http.Head()

    HTTP 中的 Head 请求方式表明只请求目标 URL 的头部信息,即 HTTP Header 而不返回 HTTPBody。 Go 内置的 net/http 包同样也提供了 http.Head() 方法,该方法同 http.Get() 方法一样,只需传入目标 URL 一个参数即可。下面的示例代码请求一个网站首页的 HTTP Header信息:

    ·(*http.Client).Do()

    在多数情况下, http.Get()和http.PostForm() 就可以满足需求,但是如果我们发起的HTTP 请求需要更多的定制信息,我们希望设定一些自定义的 Http Header 字段,比如:

     设定自定义的"User-Agent",而不是默认的 "Go http package"

     传递 Cookie

   此时可以使用net/http包http.Client对象的Do()方法来实现:

 

       除了上面介绍的基本HTTP操作,Go语言标准库也暴露了比较底层的HTTP相关库,让开发者可以基于这些库灵活定制HTTP服务器和使用HTTP服务。

       ·自定义http.Client

       前面我们使用的http.Get()、http.Post()、http.PostForm()和http.Head()方法其实都是在http.DefaultClient的基础上进行调用的,比如http.Get()等价于http.DefaultClient.Get(),依次类推。

   http.DefaultClient在字面上就向我们传达了一个消息,既然存在默认的Client,那么Http Client大概是可以自定义的。实际上确实如此,在net/http包中,的确提供了Client类型。让我们来看一看http.Client类型的结构:

   在Go语言标准库中,http.Client类型包含了3个公开数据成员:

       其中Transport类型必须实现http.RoundTripper接口。Transport指定了执行一个HTTP请求的运行机制,倘若不指定具体的Transport,默认会使用http.DefaultTransport,这意味着http.Transport也是可以自定义的。net/http包中的http.Transport类型实现了http.RoundTripper接口。

   CheckRedirect函数指定处理重定向的策略。当使用HTTP client的Get()或者是Head()方法发送HTTP请求时,若响应返回的状态码为30X(比如301/302/303/307),HTTP Client会在遵循跳转规则之前先调用这个CheckRedirect函数函数。

   Jar可用于在HTTP Client中设定Cookie,Jar的类型必须实现了http.CookieJar接口,该接口预定义了SetCookies()和Cookies()两个方法。如果Http Client中没有设定Jar,Cookie将忽略而不会发送到客户端。实际上,我们一般都用http.SetCookie()方法来设定Cookie。

   使用自定义的http.Client及其Do()方法,我们可以非常灵活的控制HTTP请求,比如发送自定义HTTP Header或是改写重定向策略等。创建自定义的HTTP Client非常简单,具体代码如下:

    ·自定义 http.Transport

   在http.Client 类型的结构定义中,我们看到的第一个数据成员就是一个 http.Transport对象,该对象指定执行一个 HTTP 请求时的运行规则。下面我们来看看 http.Transport 类型的具体结构:

   在上面的代码中,我们定义了 http.Transport 类型中的公开数据成员,下面详细说明其中的各行代码。

      Proxy func(*Request) (*url.URL, error)

   Proxy 指定了一个代理方法,该方法接受一个 *Request 类型的请求实例作为参数并返回一个最终的 HTTP 代理。如果 Proxy 未指定或者返回的 *URL 为零值,将不会有代理被启用。

      Dial func(net, addr string) (c net.Conn, err error)

   Dial 指定具体的dial()方法来创建 TCP 连接。如果不指定,默认将使用 net.Dial() 方法。

      TLSClientConfig *tls.Config

   SSL连接专用, TLSClientConfig 指定 tls.Client 所用的 TLS 配置信息,如果不指定,也会使用默认的配置。

      DisableKeepAlives bool

   是否取消长连接,默认值为 false,即启用长连接。

      DisableCompression bool

   是否取消压缩(GZip),默认值为 false,即启用压缩。

      MaxIdleConnsPerHost int

   指定与每个请求的目标主机之间的最大非活跃连接(keep-alive)数量。如果不指定,默认使用 DefaultMaxIdleConnsPerHost 的常量值。

   除了 http.Transport 类型中定义的公开数据成员以外,它同时还提供了几个公开的成员方法。

       func(t *Transport) CloseIdleConnections()。该方法用于关闭所有非活跃的连接。

       func(t *Transport) RegisterProtocol(scheme string, rt RoundTripper)。该方法可用于注册并启用一个新的传输协议,比如 WebSocket 的传输协议标准(ws),或者 FTP、 File 协议等。

       func(t *Transport) RoundTrip(req *Request) (resp *Response, err error)。用于实现 http.RoundTripper 接口。

     自定义http.Transport也很简单,如下列代码所示:         

  tr := &http.Transport{

                  TLSClientConfig: &tls.Config{RootCAs: pool},

                  DisableCompression: true,

            }

            client := &http.Client{Transport: tr}

            resp, err := client.Get("https://example.com")

     Client和Transport在执行多个 goroutine 的并发过程中都是安全的,但出于性能考虑,应当创建一次后反复使用。

   ·灵活的http.RoundTripper接口

    http.Client 定义的第一个公开成员就是一个http.Transport 类型的实例,且该成员所对应的类型必须实现http.RoundTripper接口。下面我们来看看 http.RoundTripper接口的具体定义:

 

    从上述代码中可以看到, http.RoundTripper接口很简单,只定义了一个名为RoundTrip的方法。任何实现了 RoundTrip() 方法的类型即可实现http.RoundTripper接口。前面我们看到的http.Transport类型正是实现了 RoundTrip() 方法继而实现了该接口。

   http.RoundTripper 接口定义的 RoundTrip() 方法用于执行一个独立的 HTTP 事务,接受传入的 \*Request 请求值作为参数并返回对应的 \*Response 响应值,以及一个 error 值。在实现具体的 RoundTrip() 方法时,不应该试图在该函数里边解析 HTTP 响应信息。若响应成功,error 的值必须为nil,而与返回的 HTTP 状态码无关。若不能成功得到服务端的响应,error必须为非零值。类似地,也不应该试图在 RoundTrip() 中处理协议层面的相关细节,比如重定向、认证或是 cookie 等。

   非必要情况下,不应该在 RoundTrip() 中改写传入的请求体(\*Request),请求体的内容(比如 URL 和 Header 等)必须在传入RoundTrip()之前就已组织好并完成初始化。通常,我们可以在默认的 http.Transport之包一层Transport并实现RoundTrip()方法,如以下代码所示:

package main
import(
"net/http"
)
type OurCustomTransport struct {
Transport http.RoundTripper
}
func (t *OurCustomTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
func (t *OurCustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// 处理一些事情 ...
// 发起HTTP请求
// 添加一些域到req.Header中
return t.transport().RoundTrip(req)
}
func (t *OurCustomTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func main() {
t := &OurCustomTransport{
//...
}
c := t.Client()
resp, err := c.Get("http://example.com")
// ...
}

    因为实现了http.RoundTripper 接口的代码通常需要在多个 goroutine中并发执行,因此我们必须确保实现代码的线程安全性。

2.HTTP服务端

2.1处理HTTP请求

   使用 net/http 包提供的 http.ListenAndServe() 方法,可以在指定的地址进行监听,开启一个HTTP,服务端该方法的原型如下:

   func ListenAndServe(addr string, handler Handler) error

   该方法用于在指定的 TCP 网络地址 addr 进行监听,然后调用服务端处理程序来处理传入的连接请求。该方法有两个参数:第一个参数 addr 即监听地址;第二个参数表示服务端处理程序,通常为空,这意味着服务端调用 http.DefaultServeMux 进行处理,而服务端编写的业务逻辑处理程序 http.Handle() 或 http.HandleFunc() 默认注入 http.DefaultServeMux 中,具体代码如下:

 

    http.Handle("/foo", fooHandler)

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

         fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))

    })

      log.Fatal(http.ListenAndServe(":8080", nil))

 如果想更多地控制服务端的行为,可以自定义 http.Server,代码如下:

     

 s := &http.Server{
    Addr: ":8080",
    Handler: myHandler,
    ReadTimeout: 10 * time.Second,
    WriteTimeout: 10 * time.Second,
    MaxHeaderBytes: 1 << 20,
}

log.Fatal(s.ListenAndServe())

 

2.2.处理HTTPS请求

    net/http 包还提供 http.ListenAndServeTLS() 方法,用于处理 HTTPS 连接请求:

       

 func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error

    ListenAndServeTLS() 和 ListenAndServe()的行为一致,区别在于只处理HTTPS请求。此外,服务器上必须存在包含证书和与之匹配的私钥的相关文件,比如certFile对应SSL证书文件存放路径, keyFile对应证书私钥文件路径。如果证书是由证书颁发机构签署的, certFile参数指定的路径必须是存放在服务器上的经由CA认证过的SSL证书。

   开启 SSL 监听服务也很简单,如下列代码所示:

http.Handle("/foo", fooHandler)

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
       fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

log.Fatal(http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil))

       或者是:

ss := &http.Server{
            Addr: ":10443",                
            Handler: myHandler,                
            ReadTimeout: 10 * time.Second,                
            WriteTimeout: 10 * time.Second,                
            MaxHeaderBytes: 1 << 20,         
}         
log.Fatal(ss.ListenAndServeTLS("cert.pem", "key.pem"))

 

参考:

https://www.yuque.com/docs/share/f74956e9-1c68-43fa-8a60-cab4bdbfe99d

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值