zoukankan      html  css  js  c++  java
  • Go HTTP编程

    net/http介绍

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

    HTTP服务端

    默认的Server

    首先,我们编写一个最简单的Web服务器。编写这个Web服务只需要两步:

    1. 注册一个处理器函数(注册到DefaultServeMux);

    2. 设置监听的TCP地址并启动服务;

    对应到我们的代码里就是这样的:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    //say hello to the world
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	//n, err := fmt.Fprintln(w, "hello world")
    	_, _ = w.Write([]byte("hello world"))
    }
    
    func main() {
    
    	//1.注册一个处理器函数
    	http.HandleFunc("/", sayHello)
    
    	//2.设置监听的TCP地址并启动服务
    	//参数1:TCP地址(IP+Port)
    	//参数2:handler handler参数一般会设为nil,此时会使用DefaultServeMux。
    	err := http.ListenAndServe("127.0.0.1:9000", nil)
    	if err != nil {
    		fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    }
    

    运行该程序,通过浏览器访问,可以看到hello world显示在了浏览器页面上

    ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。

    Handle和HandleFunc函数可以向DefaultServeMux添加处理器。

    http.HandleFunc

    使用Go语言中的net/http包来编写一个简单的接收HTTP请求的Server端示例,net/http包是对net包的进一步封装,专门用来处理HTTP协议的数据。具体的代码如下:

    处理器函数的实现原理:

    通过源码可知,这个函数实际上是调用了默认的serveMux的handleFunc方法, 这也解释了我们第一步里所说的默认的实际注册到DefaultServeMux

    既然说了http.ListenAndServe的第二个参数为nil时采用默认的DefaultServeMux,那么如果我们不想采用默认的,而是想自己创建一个ServerMux该怎么办呢,http给我们提供了方法

    func NewServeMux() *ServeMux
    

    NewServeMux创建并返回一个新的*ServeMux

    如果是我们自己创建的ServeMux,我们只需要简单的更新一下代码:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    //my goal is to become a gopher
    func myGoal(w http.ResponseWriter, r *http.Request) {
    	_, _ = w.Write([]byte("I wan`t to become a gopher."))
    }
    
    func main() {
    
    	//1.注册一个处理器函数
    	serveMux := http.NewServeMux()
    	serveMux.HandleFunc("/", myGoal)
    
    	//2.设置监听的TCP地址并启动服务
    	//参数1:TCP地址(IP+Port)
    	//参数2:handler 创建新的*serveMux,不使用默认的
    	err := http.ListenAndServe("127.0.0.1:9000", serveMux)
    	if err != nil {
    		fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    }
    

    运行修改后的代码,和采用默认ServeMux一样正常运行

    http.Handle

    如果是使用http的handle方法,则handle的第二个参数需要实现handler接口,要想实现这个接口,就得实现这个接口的serveHTTP方法

    package main
    
    import (
        "fmt"
        "net/http"
    )
    type MyHandler struct {}
    
    //实现Handler接口
    func (h *MyHandler) ServeHTTP (w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "goodbye")
    }
    
    func main() {
        var handler MyHandler
        http.Handle("/sayGoodbye", &handler)
        var err = http.ListenAndServe(":8080", nil)
        if err != nil {
            fmt.Printf("http server failed, err: %v
    ", err)
            return
        }
    }
    

    http.Request

    一个Web服务器最基本的工作就是接收请求,做出响应。http包帮助我们封装了一个Request结构体,我们通过这个结构体拿到很多用户的一次HTTP请求的所有信息。这个Request结构体定义如下:

    type Request struct {
    	//Method指定HTTP方法(GET、POST、PUT等)。对客户端,""代表GET。
    	Method string
    
    	// 在客户端,URL的Host字段指定了要连接的服务器,
    	// 而Request的Host字段(可选地)指定要发送的HTTP请求的Host头的值。
    	URL *url.URL
    
    	//接收到的请求的协议版本。本包生产的Request使用HTTP/1.1或者HTTP/2
    	Proto      string // "HTTP/1.0"
    	ProtoMajor int    // 1
    	ProtoMinor int    // 0
    
    	//Header字段用来表示HTTP请求的头域。
    	Header Header
    
    	//请求主题
    	Body io.ReadCloser
    
    	.....
    }
    

    我这里列举的并不是完整的Request结构体定义,只是大致的说明一下。完整的定义以及这些字段的中文含义可以查看Go语言标准库中文文档,不过需要注意的是由于中文文档更新不及时(毕竟非官方),会导致一些描述不准确,比如上面的Request结构体中的Proto请求协议版本字段在最新的Go版本中已经支持了HTTP/2,但是在其中的翻译还是停留在老版本的只支持HTTP/1.1。所以英语好的同学还是更推荐看源码里的官方文档描述。

    我们通过通过浏览器可以发现,我们一次HTTP请求会携带很多信息

    这些信息,我们可以用http.Request来获取到

    示例代码:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
        defer r.Body.Close()
    	fmt.Println("Method: ", r.Method)
    	fmt.Println("URL: ", r.URL)
    	fmt.Println("header: ", r.Header)
    	fmt.Println("body: ", r.Body)
    	fmt.Println("RemoteAddr: ", r.RemoteAddr)
    	w.Write([]byte("请求成功!!!"))
    }
    func main() {
    
    	http.HandleFunc("/", myHandler)
    	err := http.ListenAndServe("127.0.0.1:9000", nil)
    	if err != nil {
    		fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    }
    

    自定义Server

    要管理服务端的行为,可以创建一个自定义的Server:

    import (
    	"fmt"
    	"net/http"
    	"time"
    )
    type MyHandler struct {}
    
    func (h *MyHandler) ServeHTTP (w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintln(w, "hello world!")
    }
    
    func main() {
    	var handler MyHandler
    	var server = http.Server{
    		Addr:              ":8080",
    		Handler:           &handler,
    		ReadTimeout:       2 * time.Second,
    		MaxHeaderBytes: 1 << 20,
    	}
    	var err = server.ListenAndServe()
    	if err != nil {
    		fmt.Printf("http server failed, err: %v
    ", err)
    		return
    	}
    }
    

    HTTP客户端

    http包提供了很多访问Web服务器的函数,比如http.Get()http.Post()http.Head()等,读到的响应报文数据被保存在 Response 结构体中。

    我们可以看一下Response结构体的定义

    type Response struct {
    	Status     string // e.g. "200 OK"
    	StatusCode int    // e.g. 200
    	Proto      string // e.g. "HTTP/1.0"
    	ProtoMajor int    // e.g. 1
    	ProtoMinor int    // e.g. 0
    
    	Header Header
        Body io.ReadCloser
    	//...
    }
    

    上面只是Response的部分定义,完整的建议去查看源码。

    服务器发送的响应包体被保存在Body中。可以使用它提供的Read方法来获取数据内容。保存至切片缓冲区中,拼接成一个完整的字符串来查看。

    结束的时候,需要调用Body中的Close()方法关闭io。

    基本的HTTP/HTTPS请求

    Get、Head、Post和PostForm函数发出HTTP/HTTPS请求。

    resp, err := http.Get("http://example.com/")
    ...
    resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
    ...
    resp, err := http.PostForm("http://example.com/form",
    	url.Values{"key": {"Value"}, "id": {"123"}})
    

    程序在使用完response后必须关闭回复的主体。

    resp, err := http.Get("http://example.com/")
    if err != nil {
    	// handle error
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    // ...
    

    GET请求示例

    使用net/http包编写一个简单的发送HTTP请求的Client端,代码如下:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    )
    
    func main() {
    
    	resp, err := http.Get("http://127.0.0.1:9000")
    	if err != nil {
    		fmt.Printf("http.Get()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    	defer resp.Body.Close()
    
    	body, err := ioutil.ReadAll(resp.Body)
    
    	if err != nil {
    		fmt.Printf("ioutil.ReadAll()函数执行出错,错误为:%v
    ", err)
    		return
    	}
    
    	fmt.Println(string(body))
    }
    

    将上面的代码保存之后编译成可执行文件,执行之后就能在终端打印请求成功!!!网站首页的内容了,我们的浏览器其实就是一个发送和接收HTTP协议数据的客户端,我们平时通过浏览器访问网页其实就是从网站的服务器接收HTTP数据,然后浏览器会按照HTML、CSS等规则将网页渲染展示出来。

    带参数的GET请求示例

    关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"net/url"
    )
    
    func main() {
    
    	//1.处理请求参数
    	params := url.Values{}
    	params.Set("name", "itbsl")
    	params.Set("hobby", "fishing")
    
    	//2.设置请求URL
    	rawUrl := "http://127.0.0.1:9000"
    	reqURL, err := url.ParseRequestURI(rawUrl)
    	if err != nil {
    		fmt.Printf("url.ParseRequestURI()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    
    	//3.整合请求URL和参数
    	//Encode方法将请求参数编码为url编码格式("bar=baz&foo=quux"),编码时会以键进行排序。
    	reqURL.RawQuery = params.Encode()
    
    	//4.发送HTTP请求
    	//说明: reqURL.String() String将URL重构为一个合法URL字符串。
    	resp, err := http.Get(reqURL.String())
    	if err != nil {
    		fmt.Printf("http.Get()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    	defer resp.Body.Close()
    	
        //5.一次性读取响应的所有内容
    	body, err := ioutil.ReadAll(resp.Body)
    
    	if err != nil {
    		fmt.Printf("ioutil.ReadAll()函数执行出错,错误为:%v
    ", err)
    		return
    	}
    
    	fmt.Println(string(body))
    }
    

    对应的Server端代码如下:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
    	defer r.Body.Close()
    	params := r.URL.Query()
    	fmt.Fprintln(w, "name:", params.Get("name"), "hobby:", params.Get("hobby"))
    }
    func main() {
    
    	http.HandleFunc("/", myHandler)
    	err := http.ListenAndServe("127.0.0.1:9000", nil)
    	if err != nil {
    		fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v
    ", err)
    		return
    	}
    }
    

    Post请求示例

    上面演示了使用net/http包发送GET请求的示例,发送POST请求的示例代码如下:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"strings"
    )
    
    // net/http post demo
    
    func main() {
    	url := "http://127.0.0.1:9090/post"
    	// 表单数据
    	//contentType := "application/x-www-form-urlencoded"
    	//data := "name=小王子&age=18"
    	// json
    	contentType := "application/json"
    	data := `{"name":"小王子","age":18}`
    	resp, err := http.Post(url, contentType, strings.NewReader(data))
    	if err != nil {
    		fmt.Println("post failed, err:%v
    ", err)
    		return
    	}
    	defer resp.Body.Close()
    	b, err := ioutil.ReadAll(resp.Body)
    	if err != nil {
    		fmt.Println("get resp failed,err:%v
    ", err)
    		return
    	}
    	fmt.Println(string(b))
    }
    

    对应的Server端HandlerFunc如下:

    func postHandler(w http.ResponseWriter, r *http.Request) {
    	defer r.Body.Close()
    	// 1. 请求类型是application/x-www-form-urlencoded时解析form数据
    	r.ParseForm()
    	fmt.Println(r.PostForm) // 打印form数据
    	fmt.Println(r.PostForm.Get("name"), r.PostForm.Get("age"))
    	// 2. 请求类型是application/json时从r.Body读取数据
    	b, err := ioutil.ReadAll(r.Body)
    	if err != nil {
    		fmt.Println("read request.Body failed, err:%v
    ", err)
    		return
    	}
    	fmt.Println(string(b))
    	answer := `{"status": "ok"}`
    	w.Write([]byte(answer))
    }
    

    自定义Client

    要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:

    client := &http.Client{
    	CheckRedirect: redirectPolicyFunc,
    }
    resp, err := client.Get("http://example.com")
    // ...
    req, err := http.NewRequest("GET", "http://example.com", nil)
    // ...
    req.Header.Add("If-None-Match", `W/"wyzzy"`)
    resp, err := client.Do(req)
    // ...
    

    自定义Transport

    要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个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同时使用。出于效率考虑,应该一次建立、尽量重用。

  • 相关阅读:
    Openstack API 开发 快速入门
    virtualBox虚拟机到vmware虚拟机转换
    使用Blogilo 发布博客到cnblogs
    Openstack Troubleshooting
    hdoj 1051 Wooden Sticks(上升子序列个数问题)
    sdut 2430 pillars (dp)
    hdoj 1058 Humble Numbers(dp)
    uva 10815 Andy's First Dictionary(快排、字符串)
    sdut 2317 Homogeneous squares
    hdoj 1025 Constructing Roads In JGShining's Kingdom(最长上升子序列+二分)
  • 原文地址:https://www.cnblogs.com/itbsl/p/12175645.html
Copyright © 2011-2022 走看看