Golang net/http包

介绍

这个包的作用主要是用来发送http请求和接受http请求的。

  • 作为客户端:它去发送一个请求,拿到返回

  • 作为服务端:直接启动web服务,然后根据其他人的请求返回不同的结果

.
├── ClientGet
│ └── main.go // 发送get请求
├── ClientPost
│ └── main.go // 发送post请求
├── Server
│ └── main.go // web服务

Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。

服务器

Go 语言标准库 net/http 包提供了非常易用的接口,如下所示,我们可以利用标准库提供的功能快速搭建新的 HTTP 服务:

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

上述的 main 函数只调用了两个标准库提供的函数,它们分别是用于注册处理器的 net/http.HandleFunc 函数和用于监听和处理器请求的 net/http.ListenAndServe,多数的服务器框架都会包含这两类接口,分别负责注册处理器和处理外部请求,这一种非常常见的模式,我们在这里也会按照这两个维度介绍标准库如何支持 HTTP 服务器的实现。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) 这个函数的第一个参数是字符串类型,第二个参数是函数类型。

注册处理器

HTTP 服务是由一组实现了 net/http.Handler 接口的处理器组成的,处理 HTTP 请求时会根据请求的路由选择合适的处理器:

web服务

  • 客户端请求信息封装在 http.Request 对象中(在request对象当中,能够拿到其header,body,传参)

  • 服务端返回的响应报文会被保存在http.Response结构体中

  • 发送给客户端响应的并不是http.Response,而是通过http.ResponseWriter接口来实现的

方法签名 描述

现在要写服务端,那么需要将net/http包引入进来。

其实在各种的交互当中,接口和接口,接口和前端,前端和后端,全部都是使用json。json.NewEncoder(w).Encode(d)的好处是可以帮助你转化为json。

注意⚠️get参数都在其url里面。

HandleFunc其实也就定义了请求什么目录,那么请求了这个目录我会怎么去响应给你,具体响应交给具体的函数进行处理。

w http.ResponseWriter 借助w写入到response里面去,就是写回给调用方,调用方传递的request参数全部在r里面。

这里请求多个目录可以定义多个handler。

因为已经写好了路由,那么http.ListenAndServe(":8080", nil)里面传入的是nil。

r *http.Request 服务端可以通过r拿到请求的相应的数据,比如头部和body然后进行处理。

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

type Data struct {
    Name string `json:"name"`
}

// 这里接受两个参数是固定用法
// http.ResponseWriter是一个接口,不能使用指针类型。它有不同的实现,它作用是用来返回给客户端内容
// http.Request 指针类型,因为是结构体类型,从该对象当中拿到请求信息,其实也就是一个去拿请求信息,一个是用来返回的
func dealGetReqHandler(w http.ResponseWriter, r *http.Request) {

    query := r.URL.Query() //用于拿到?号之后的参数
    if len(query["name"]) > 0 {  //type Values map[string][]string
        name := query["name"][0]
        fmt.Println("通过字典下标获取", name)
    }

    //发出get请求的参数name
    name2 := query.Get("name")
    fmt.Println("通过get方式获取:", name2)

    //上面是针对request的,下面是响应针对response的

    //返回响应吗
    w.WriteHeader(http.StatusOK)

    //返回方法1:返回响应内容,字符串类型
    //w.Write([]byte(name2))

    //返回的是结构体,其实也就是json
    d := &Data{
        Name: name2,
    }
    json.NewEncoder(w).Encode(d)

}

func delPostReqHandler(w http.ResponseWriter, r *http.Request) {
//获取请求体数据,请求体的类型是reader类型
    bodyContent, _ := ioutil.ReadAll(r.Body)
    strData := string(bodyContent)

    var d Data
    json.Unmarshal([]byte(strData), &d)
    json.NewEncoder(w).Encode(d)

}

func main() {
    //注册路由,注册处理器
    http.HandleFunc("/req/get", dealGetReqHandler)
    http.HandleFunc("/req/post", delPostReqHandler)
    //后面nile是全局的handler,没必要,因为局部已经注册了
    http.ListenAndServe(":8000", nil)
}

这样就可以并发的去处理多个请求,对于每个请求,其实它会单独的去开辟go routine协程去处理它。

请求数据

ClientGet

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)

func ReqGet() {
    //定义发起请求的目标
    apiUrl := "http://127.0.0.1:8000/get"

    //设置请求参数
    data := url.Values{}
    data.Set("name", "lucas")

    //组装url和参数
    u, _ := url.ParseRequestURI(apiUrl)
    u.RawQuery = data.Encode()
    fmt.Println("请求路由为:", u, u.String())

    //发起请求
    resp, _ := http.Get(u.String())

    //拿到响应
    b, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(b))
    //正常情况下,拿到byte数组之后,反序列化到自定义结构体去使用
   
   //还可以去遍历响应的头部,这里除了可以打印头部还可以去打印协议版本号和响应码
    for k, v := range resp.Header {
        fmt.Println(k, v)
    }
    fmt.Println(r.Proto, r.StatusCode)
    /*
        Date [Mon, 10 Apr 2023 03:13:13 GMT]    
        Content-Length [14]                     
        Content-Type [text/plain; charset=utf-8]
        HTTP/1.1 200
    */

}

func main() {
    ReqGet()
}

为url添加参数

    apiUrl := "http://127.0.0.1:8000/post"

    param := url.Values{}
    param.Set("name", "lucas")
    param.Set("age", "30")

    u, _ := url.ParseRequestURI(apiUrl)
    u.RawQuery = param.Encode()

    fmt.Println(u.String())

http://127.0.0.1:8000/post?age=30&name=lucas

ClientPost

func Post(url string, contentType string, body io.Reader) (resp *Response, err error)

type Reader interface {
    Read(p []byte) (n int, err error)
}

func NewReader(s string) *Reader

方法对象: (*Reader):
Read(b []byte) (n int, err error)
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

func main() {
    apiUrl := "http://0.0.0.0:8000/req/post"

    //表单数据定义
    contentType := "application/json"
    data := `{"name":"tony"}`

    //发起请求,固定用法   将string类型转化为reader类型
    response, _ := http.Post(apiUrl, contentType, strings.NewReader(data))
    b, _ := ioutil.ReadAll(response.Body)
    fmt.Println(string(b))

}

客户端发起稍微复杂一些的请求

http.Post
import (
    "net/http"
    "net/url"
)

data := url.Values{"start":{"100"}, "hobby":{"xxxx"}}
body := strings.NewReader(data.Encode())
resp, err := http.Post("127.0.0.1:9338", "application/x-www-form-urlencoded", body)

net/http包没有封装直接使用请求带header的get或者post方法,所以,要想请求中带header,只能使用NewRequest方法

Golang通过http.NewRequest实现模拟请求,添加请求头和请求参数:

func complexHttpRequest() {

    reader := strings.NewReader(`"data":"123456"`)
    //构造request请求,返回request的指针
    if r, err := http.NewRequest("POST", "http://127.0.0.1:8000/post", reader); err != nil {
        panic(err)
    } else {
        //自定义 添加头部(伪造header)
        r.Header.Add("User-Agent", "postman")
        r.Header.Add("MyHeaderKey", "key")
        r.Header.Add("Content-Type", "application/json")
        r.Header.Add("token", "lucas123")

        //自定义cookie
        r.AddCookie(&http.Cookie{
            Name:    "auth",
            Value:   "passwd",
            Expires: time.Now().Add(time.Duration(time.Hour)),
        })

        //上面构造好了request,那么要将request发送出去需要构造client,下面设置了超时时间
        client := &http.Client{
            Timeout: 100 * time.Millisecond,
        }
        if resp, err := client.Do(r); err != nil {
            fmt.Println(err)
        } else {
            defer resp.Body.Close()
            d, _ := ioutil.ReadAll(resp.Body)
            fmt.Println(string(d))
        }
    }

}

服务端去拿到客户端的cookie和头部信息

func delPostHandler(w http.ResponseWriter, r *http.Request) {
    bodyContent, _ := ioutil.ReadAll(r.Body)
    strData := string(bodyContent)
    fmt.Println(strData)
    fmt.Fprint(w, "success")

    for k, v := range r.Header {
        fmt.Println(k, v)
    }

    for _, v := range r.Cookies() {
        fmt.Println(v)
    }
}
Myheaderkey [key]
Cookie [auth=passwd]
Myheaderkey [key]
User-Agent [postman]
Content-Length [15]
auth=passwd

最后一个例子总结:

func DownloadString(remoteUrl string,queryValues url.Values) (body []byte,err error){
    client := &http.Client{};
    body = nil;
    uri,err := url.Parse(remoteUrl);
    if(err != nil){
        return ;
    }
    if(queryValues != nil){
        values := uri.Query();
        if(values != nil){
            for k,v := range values {
                queryValues[k] = v;
            }
        }
        uri.RawQuery = queryValues.Encode();
    }
    reqest, err := http.NewRequest("GET",uri.String(),nil);
    reqest.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    reqest.Header.Add("Accept-Encoding", "gzip, deflate");
    reqest.Header.Add("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
    reqest.Header.Add("Connection", "keep-alive");
    reqest.Header.Add("Host", uri.Host);
    reqest.Header.Add("Referer", uri.String());
    reqest.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0");
 
    response, err := client.Do(reqest)
    defer response.Body.Close();
    if(err != nil){
        return ;
    }
 
    if response.StatusCode == 200 {
        switch response.Header.Get("Content-Encoding") {
        case "gzip":
            reader, _ := gzip.NewReader(response.Body)
            for {
                buf := make([]byte, 1024)
                n, err := reader.Read(buf)
 
                if err != nil && err != io.EOF {
                    panic(err)
                }
 
                if n == 0 {
                    break
                }
                body = append(body,buf...);
            }
        default:
            body, _ = ioutil.ReadAll(response.Body)
 
        }
    }
    return ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值