Go Web基础

处理器和处理器函数

  • 处理器是一个拥有ServeHTTP方法的接口

    接收两个参数:ResponseWriter接口和指向Request结构的指针

    也就是,形如ServeHTTP(http.ResponseWriter, *http.Request)就是一个处理器

  • 处理器函数是与处理器具有相同行为的函数,它们接收ResponseWriter和指向Request结构的指针作为参数

  • 更详细的:关于Handle、HandleFunc、Handler和HandlerFunc | ruomu (gitee.io)

通过下面代码直观的感受:

package main

import (
	"fmt"
	"net/http"
)

// 处理器和处理器函数

type HelloHandler struct {}	// 这是一个处理器
type WorldHandler struct {}	// 同样的也是一个处理器

// 为它们定义动作,实现 Handler 中的 ServeHttp 方法
func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello")
}

func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "World!")
}

// 使用处理器函数 处理请求
func helloFunc(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "hello function!")
}

func main() {
	// 使用处理器
	hello := HelloHandler{}
	world := WorldHandler{}
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	// 直接绑定 处理器
	http.Handle("/hello", &hello)
	http.Handle("/world", &world)
	// 使用 处理函数
	http.HandleFunc("/hellof", helloFunc)
	
	server.ListenAndServe()

}

实际上,golang中的HandlerFunc函数类型将带有正确签名的函数f转换成一个带有方法fHandler,也就是说golang自动用这个处理器函数生成了一个处理器。

但是,处理器函数并不能完全替代处理器,即使看起来整洁一些。

串联多个处理器和处理器函数

先看代码:

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"runtime"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hello!")
}

//
// @Name:	log
// @Desc: 	记录一个handler function的日志
// @Param:	h
// @Return:	http.HandlerFunc
// @Notice: 使用 runtime.FuncForPC 获取正在调用系统日志,也就是该函数的一些信息
//
func log(h http.HandlerFunc) http.HandlerFunc {
    // 使用匿名函数进行返回
	return func(w http.ResponseWriter, r *http.Request) {
		name := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
		fmt.Println("Hanlder function called - " + name)
		h(w, r)
	}
}

//
// @Name:	main
// @Desc: 	串联两个处理器函数
// @Notice:
//
func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	// log 返回一个 handlerfunc 所以它满足类型 handler
	http.HandleFunc("/hello", log(hello))
	server.ListenAndServe()
}

利用go中函数式编程的特性,将不同的函数进行串联,上面的log()函数接收一个函数作为参数,返回另一个函数。也就可以说,这段代码将log()hello()进行串联。

再比如说,我们还需要使用一个protect()函数在调用传入的处理器之前去验证用户的身份,进行访问控制:

package chaining_func

import (
	"fmt"
	"net/http"
)

type HelloHandler struct {}

func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hello!")
}

func log1(h http.Handler) http.Handler {
    // 这里的 http.HandlerFunc 将一个 Function 转换成 Handler
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("Handler called - %T\n", h)
		h.ServeHTTP(w, r)
	})
}

func protect(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Do some protect...")
		h.ServeHTTP(w, r)
	})
}

func chainingHandlers() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	hello := HelloHandler{}
	http.Handle("/hello", protect(log1(hello)))
	server.ListenAndServe()
}

从上面两个代码中,对处理handlerfunchandler都进行了演示。对于处理器和处理器函数的关系还是值得好好思考下的。

函数

  • http.Server():对服务器进行详细配置,比如请求读取操作和响应写入操作的超时时间、日志记录器、服务器地址、处理器、TLS配置、连接状态…
  • http.ListenAndServe():配置服务器地址、处理器
  • http.ListenAndServeTLS():使用SSL证书和服务器私钥配置https服务

关于路由

  • /hello/hello/有什么区别:前者会准确的进行路径匹配、后者可以用前缀进行匹配

值得注意的问题点

  • GETPOST都可以发送表单,但是由于GET请求不会有请求主体,所以它的数据是包含在URL中传输的

  • 几种不同的拿字段的方式:

Licensed under CC BY-NC-SA 4.0
自认为是幻象波普星的来客
Built with Hugo
主题 StackJimmy 设计