介绍
这个包的作用主要是用来发送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)) 这个函数的第一个参数是字符串,第二个参数是函数。
注册处理器
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。
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数组之后,反序列化到自定义结构体去使用
}
func main() {
ReqGet()
}
ClientPost
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))
}