快速上手GO的net/http包

作者:

文章目录
  • 创建简单的GOHTTP服务 func main() { http.HandleFunc(“/hello”, sayHello) http.ListenAndServe(“:8080”, nil) //创建基本服务 } func sayHello(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“Hello, World!”)) } 访问8080/hello进行测试 Handler接口定义:(这部分后面又详细解释) type Handler interface { ServeHTTP(ResponseWriter, *Request) } //只要有ServeHTTP方法就行 可以自己实现这个接口 同时http提供了handlerFunc结构体 type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } //本质上就是调用自身,因为也是一个函数,不过serveHTTP的内容自己可以定义改动 和之前的HandleFunc区分,HandleFunc是用来给不同路径绑定方法的,少一个r 那么只要满足Handlerfunc 的签名形式,就可以进行类型转换 类型转换的正常的理解例子 type yes func(int, int) int func (y yes) add(a, b int) int { return a + b } func multiply(a, b int) int { fmt.Println(a * b) return a * b } func main() { multiply(1, 2) //2 ans := yes(multiply) //将multiply进行转换 res := ans.add(1, 2) fmt.Println(res) //3 } http.HandleFunc(“/hello”, hello) 这个后面的签名只要是 func(ResponseWriter, *Request)就可以了但是 http.ListenAndServe(“:8080”, referer) 这个后面的函数需要是满足Handler接口,有serveHTTP方法 尝试搭建检测是在query中有name = red即http://localhost:8080/hello?name=red发现会有重复覆盖路由的问题,因为listenandServe会拦截所有的路由,后面再解决 type CheckQueryName struct { wantname string handler http.Handler } func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() //获取get请求的query name := queryParams.Get(“name”) if name == “red” { this.handler.ServeHTTP(w, r) //其实就是调用本身,下面变为checkforname了 } else { w.Write([]byte(“not this name”)) } } func checkforname(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“check is ok”)) } func hello(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“hello”)) } func main() { thecheck := &CheckQueryName{ //用&因为serveHTTP方法定义在指针接收器上 wantname: “red”, handler: http.HandlerFunc(checkforname), } http.HandleFunc(“/hello”, hello) //满足func(ResponseWriter, *Request)签名就可以 http.ListenAndServe(“:8080”, thecheck) //直接监视8080的所有端口,拦截所有路由 }
  • DefaultServeMux一般不会使用,因为会有冲突等等问题,所以一般用NewServeMux直接创建 type apiHandler struct{} func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set(“Content-Type”, “application/json”) fmt.Fprintf(w, `{“message”: “API response”}`) } func main() { mux := http.NewServeMux() mux.Handle(“/api/”, apiHandler{}) //多引入结构体,后面会知道有好处 // mux.HandleFunc(“/api/”, func(w http.ResponseWriter, req *http.Request) { // w.Header().Set(“Content-Type”, “application/json”) // fmt.Fprintf(w, `{“message”: “API response from HandleFunc”}`) // }) //和上面等效 mux.HandleFunc(“/”, func(w http.ResponseWriter, req *http.Request) { if req.URL.Path != “/” { http.NotFound(w, req) return } fmt.Fprintf(w, “Welcome to the home page!”) }) fmt.Println(“Server running on :8080”) http.ListenAndServe(“:8080”, mux) //用server:= &http.Server创建地址和handler,然后server.ListenAndServe也是一种表现方式 } mux和http.HandleFunc()的区别:mux 可以创建多个路由器实例,用于不同的目的。同时可以为不同的路由器配置不同的中间件(用着先) 第三方有库httprouter,比如可以解决url不能是变量代表的问题,了解就行更多都是使用restful API进行开发的~目前的了解有个概念就行
  • 请求分为:请求头,请求URL,请求体等
  • 补充:fmt.Fprint和fmt.Fprintln能够写入ResponseWriter是因为ResponseWriter实现了io.Writer接口,fmt.Fprint/Fprintln将数据按格式转换为字节流(如字符串、数字等),最终调用io.Writer的Write方法
  • 目录
    • 基础快速了解
      • 编写简单的GET请求客户端
      • 编写自定义的GET请求客户端
      • 编写默认的post请求客户端
    • 多路复用器
      • 处理器函数
        • Handle
        • HandlleFunc
        • Handler
        • HandlerFunc
      • 处理请求
        • html表单的enctype属性
      • ResponseWriter接口涉及方法
        • Writeheader
        • Header
        • write

      针对GO中net/http包的学习笔记

      创建简单的GOHTTP服务

      func main() {  
          http.HandleFunc("/hello", sayHello)  
          http.ListenAndServe(":8080", nil) //创建基本服务  
      }  
        
      func sayHello(w http.ResponseWriter, r *http.Request) {  
          w.Write([]byte("Hello, World!"))  
      }
      

      访问8080/hello进行测试

      Handler接口定义:(这部分后面又详细解释)

      type Handler interface {  
          ServeHTTP(ResponseWriter, *Request)  
      }
      //只要有ServeHTTP方法就行
      

      可以自己实现这个接口

      同时http提供了handlerFunc结构体

      type HandlerFunc func(ResponseWriter, *Request)
      // ServeHTTP calls f(w, r).
      func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
      f(w, r)
      }
      //本质上就是调用自身,因为也是一个函数,不过serveHTTP的内容自己可以定义改动
      

      和之前的HandleFunc区分,HandleFunc是用来给不同路径绑定方法的,少一个r

      那么只要满足Handlerfunc 的签名形式,就可以进行类型转换

      • 类型转换的正常的理解例子
      type yes func(int, int) int  
      func (y yes) add(a, b int) int {  
          return a + b  
      }  
      func multiply(a, b int) int {  
      	fmt.Println(a * b)
          return a * b  
      }  
      func main() {  
          multiply(1, 2)   //2
          ans := yes(multiply) //将multiply进行转换  
          res := ans.add(1, 2)  
          fmt.Println(res)   //3
      }
      
      
      http.HandleFunc("/hello", hello)
      

      这个后面的签名只要是 func(ResponseWriter, *Request)就可以了
      但是

      http.ListenAndServe(":8080", referer)
      

      这个后面的函数需要是满足Handler接口,有serveHTTP方法

      尝试搭建检测是在query中有name = red
      即http://localhost:8080/hello?name=red
      发现会有重复覆盖路由的问题,因为listenandServe会拦截所有的路由,后面再解决

      type CheckQueryName struct {
      	wantname string
      	handler  http.Handler
      }
      
      func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      	queryParams := r.URL.Query() //获取get请求的query
      	name := queryParams.Get("name")
      	if name == "red" {
      		this.handler.ServeHTTP(w, r) //其实就是调用本身,下面变为checkforname了
      	} else {
      		w.Write([]byte("not this name"))
      	}
      }
      
      func checkforname(w http.ResponseWriter, r *http.Request) {
      	w.Write([]byte("check is ok"))
      }
      
      func hello(w http.ResponseWriter, r *http.Request) {
      	w.Write([]byte("hello"))
      }
      
      func main() {
      	thecheck := &CheckQueryName{ //用&因为serveHTTP方法定义在指针接收器上
      		wantname: "red",
      		handler:  http.HandlerFunc(checkforname),
      	}
      	http.HandleFunc("/hello", hello)       //满足func(ResponseWriter, *Request)签名就可以
      	http.ListenAndServe(":8080", thecheck) //直接监视8080的所有端口,拦截所有路由
      }
      

      利用defaultclient或者自己定义client都可以

      func main() {
      	resp, err := http.DefaultClient.Get("https://api.github.com")
      	if err != nil {
      		panic(err)
      	}
      	defer resp.Body.Close()
      	body, err := ioutil.ReadAll(resp.Body)
      	if err != nil {
      		panic(err)
      	}
      	fmt.Println(string(body))
      	time.Sleep(time.Second * 2)  //等待获取请求
      }
      

      但是如果把网址换成baidu.com就会获取不到,这是因为转发,以及没有User-agent的问题

      利用http.Client可以进行自定义

      func main() {
      	client := &http.Client{
      		// 允许重定向
      		CheckRedirect: func(req *http.Request, via []*http.Request) error {
      			return nil
      		},
      	}
      
      	req, err := http.NewRequest("GET", "https://www.baidu.com", nil)
      	if err != nil {
      		panic(err)
      	}
      	// 添加请求头
      	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
      
      	resp, err := client.Do(req) 
      	//do执行HTTP请求的整个周期包括请求准备,建立连接,发送请求,请求重定向,接收响应等等
      	defer resp.Body.Close()
      	body, err := ioutil.ReadAll(resp.Body)
      	if err != nil {
      		panic(err)
      	}
      	fmt.Println(string(body))
      	time.Sleep(time.Second * 2)
      }
      

      发现可以接受到baidu的网页html信息

      func main() {
      	postData := strings.NewReader(`{"name": "张三", "age": 25}`)
      	resp, err := http.DefaultClient.Post(
      		"http://localhost:8080/users",
      		"application/json",  
      		postData,
      	)
      	if err != nil {
      		fmt.Printf("POST请求失败: %vn", err)
      		return
      	}
      	defer resp.Body.Close()
      
      	body, _ := ioutil.ReadAll(resp.Body)
      	fmt.Printf("POST响应: %sn", string(body))
      }
      

      string.NewReader是一种方法将格式改为io reader可以读取的形式,接收的话也可以postData.Read读取。
      这一类的方式比较多,不一一汇总

      对应的server.go (需要在终端中go run server.go )两个终端分别运行服务端,客户端

      func main() {
      	// 处理 /users 路径的 POST 请求
      	http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
      		// 只允许 POST 方法
      		if r.Method != http.MethodPost {
      			w.WriteHeader(http.StatusMethodNotAllowed)
      			fmt.Fprintf(w, "只支持 POST 方法")
      			return
      		}
      		// 读取请求体
      		body, err := ioutil.ReadAll(r.Body)
      		if err != nil {
      			w.WriteHeader(http.StatusBadRequest)
      			fmt.Fprintf(w, "读取请求失败: %v", err)
      			return
      		}
      		defer r.Body.Close()
      		// 解析 JSON 数据,放入user中
      		var user User
      		if err := json.Unmarshal(body, &user); err != nil {
      			w.WriteHeader(http.StatusBadRequest) //写入状态码
      			fmt.Fprintf(w, "JSON 解析失败: %v", err)
      			return
      		}
      		// 设置响应头
      		w.Header().Set("Content-Type", "application/json")
      		// 构造响应数据
      		response := map[string]interface{}{
      			"message": "success",
      			"data": map[string]interface{}{
      				"name": user.Name,
      				"age":  user.Age,
      			},
      		}
      
      		// 返回 JSON 响应
      		json.NewEncoder(w).Encode(response) //将 response 对象转换为 JSON 格式并写入响应
      		//等价于:
      		// jsonData, err := json.Marshal(response)
      		// if err != nil {
      		// 	w.WriteHeader(http.StatusInternalServerError)
      		// 	return
      		// }
      		// w.Write(jsonData)
      	})
      
      	// 启动服务器
      	fmt.Println("服务器启动在 :8080 端口...")
      	if err := http.ListenAndServe(":8080", nil); err != nil {
      		panic(err)
      	}
      }
      

      DefaultServeMux一般不会使用,因为会有冲突等等问题,所以一般用NewServeMux直接创建

      type apiHandler struct{}
      
      func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      	w.Header().Set("Content-Type", "application/json")
      	fmt.Fprintf(w, `{"message": "API response"}`)
      }
      
      func main() {
      	mux := http.NewServeMux()
      	mux.Handle("/api/", apiHandler{})  //多引入结构体,后面会知道有好处
      	// mux.HandleFunc("/api/", func(w http.ResponseWriter, req *http.Request) {
          //     w.Header().Set("Content-Type", "application/json")
          //     fmt.Fprintf(w, `{"message": "API response from HandleFunc"}`)
          // })   //和上面等效
      	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
      		if req.URL.Path != "/" {
      			http.NotFound(w, req)
      			return
      		}
      		fmt.Fprintf(w, "Welcome to the home page!")
      	})
      
      	fmt.Println("Server running on :8080")
      	http.ListenAndServe(":8080", mux)
      	//用server:= &http.Server创建地址和handler,然后server.ListenAndServe也是一种表现方式
      	
      }
      

      mux和http.HandleFunc()的区别:
      mux 可以创建多个路由器实例,用于不同的目的。同时可以为不同的路由器配置不同的中间件(用着先)

      第三方有库httprouter,比如可以解决url不能是变量代表的问题,了解就行
      更多都是使用restful API进行开发的~目前的了解有个概念就行

      注册处理器过程中调用的函数:Handle

      type username struct {
      	name string
      }
      
      func (this *username) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      	fmt.Fprintf(w, "%s", this.name)
      }
      
      func main() {
      	mux := http.NewServeMux()
      	mux.Handle("/jack", &username{name: "jack"}) //会调用对应的serveHTTP方法
      	mux.Handle("/lily", &username{name: "lily"})
      	//可以为不同的路径使用相同的处理器结构体,但传入不同的参数
      	//这就是比用handleFunc()更灵活的地方
      	server := &http.Server{
      		Addr:    ":8080",
      		Handler: mux,
      	}
      	if err := server.ListenAndServe(); err != nil { //防止错误
      		panic(err)
      	}
      }
      

      处理器函数:HandleFunc
      (注意不是HandlerFunc,没有r !! HandleFunc 是处理器函数)
      之前已经学习过,定义就是
      func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

      深入源代码会发现内部也是借助serveMux对象,从而实现了Handler的ServeHTTP()方法的

      Handler就是处理器接口,实现ServeHTTP方法的,之前展示过

      type Handler interface {
      	ServeHTTP(ResponseWriter, *Request)
      }
      

      HandlerFunc是结构体,用于实现接口的
      定义:

      type HandlerFunc func(ResponseWriter, *Request)
      

      用于连接处理器Handle和处理器函数HandleFunc,它实现了 Handler 接口,使得函数可以直接当作处理器使用:

      • 理解“连接连接处理器Handle和处理器函数HandleFunc”:(之前也学过)
      // 方式一:普通函数
      func hello(w http.ResponseWriter, r *http.Request) {
          fmt.Fprintf(w, "Hello!")
      }
      // 注册方式一:将函数转换为 HandlerFunc
      http.Handle("/hello", http.HandlerFunc(hello))
      
      // 方式二:直接使用 HandleFunc
      http.HandleFunc("/hello", hello)
      

      请求分为:请求头,请求URL,请求体等

      在postman的body部分可以查看

      • application/x-www-form-urlencode
        url方式编码,较为通用,get和post都可以用
      • multipart/form-data
        通常适配post方法提交
      • text/plain
        适合传递大量数据

      • 补充:
        fmt.Fprint和fmt.Fprintln能够写入ResponseWriter是因为ResponseWriter实现了io.Writer接口,fmt.Fprint/Fprintln将数据按格式转换为字节流(如字符串、数字等),最终调用io.Writer的Write方法

      curl -i localhost:8080/noAuth 或者使用postman进行验证

      func noAuth(w http.ResponseWriter, r *http.Request) {
      	w.WriteHeader(401)
      	fmt.Fprint(w, "没有授权,你需要认证后访问")
      }
      
      func main() {
      	http.HandleFunc("/noAuth", noAuth)
      	err := http.ListenAndServe(":8080", nil)
      	if err != nil {
      		fmt.Println(err)
      	}
      }
      
      

      调用了Writeheader后的话就不能对响应头进行修改了
      curl -i http://localhost:8081/redirect (可以直接看到301)或者postman验证

      • 重定向代码
      func Redirect(w http.ResponseWriter, r *http.Request) {
      	w.Header().Set("Location", "http://localhost:8080/hello")
      	// 必须使用包括http的完整的URL!
      	w.WriteHeader(301)
      }
      func main() {
      	http.HandleFunc("/redirect", Redirect)
      	if err := http.ListenAndServe(":8081", nil); err != nil {
      		panic(err)
      	}
      }
      
      • 主服务代码
      func sayHello(w http.ResponseWriter, r *http.Request) {
      	w.Write([]byte("hello !!"))
      }
      
      func main() {
      	http.HandleFunc("/hello", sayHello)
      	http.ListenAndServe(":8080", nil) //创建基本服务
      }
      
      

      之前都有demo,就是写入返回,注意需要是[]byte()这样的表示形式
      如果不知道content-type格式可以通过数据的前512 比特进行确认

      除了一般的文本字符串之外,还可以返回html和json,下面给出json的示范

      type language struct {
      	Language string `json:"language"` //反引号
      	 // 字段名首字母需要大写才能被 JSON 序列化!!!!
      }
      
      func uselanguage(w http.ResponseWriter, r *http.Request) {
      	uselanguageis := language{Language: "en"}
      	message, err := json.Marshal(uselanguageis)
      	if err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              return
          }
      	w.Header().Set("Content-Type", "application/json") //通过json形式传递
      	w.Write(message)
      }
      
      func main() {
      	http.HandleFunc("/lan", uselanguage)
      	if err := http.ListenAndServe(":8080", nil); err != nil {
      		panic(err)
      	}
      }
      

      注意一些格式上的细节,比如字段名首字母需要大写才能被 JSON 序列化

      到此这篇关于快速上手GO的net/http包的文章就介绍到这了,更多相关GO net/http包内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

      您可能感兴趣的文章:

      • Go 1.22对net/http包的路由增强功能详解
      • golang内置net/http包的使用详解
      • 详解Golang net/http包中的RoundTripper接口
      • 详解Go语言中net/http包的使用
      • 一文详解Golang中net/http包的实现原理

      站内搜索