Go网络编程之HTTP编程

来源:互联网 发布:url encode 加密 java 编辑:程序博客网 时间:2024/06/05 20:26

     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 mainimport("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"))



原创粉丝点击