• 《Go Web 编程》之第3章 接收请求


    第3章 接收请求

    3.1 net/http标准库

    HTTP,无连接协议(connection-less protocol),两次请求间一无所知,通过cookie实现数据持久化。

    • Client、Response、Header、Request和Cookie支持客户端。
    • Server、ServeMux、Handler/HandlerFunc、ResponseWriter、Header、Request和Cookie支持服务端。

    3.2 Go构建服务器

    客户端与多路复用器交互(请求和响应);
    多路复用器与不同处理器交互;
    处理器与模板引擎、模型交互;
    模板引擎与模板交互、模型与数据库交互。

    3.2.1 Go Web服务

    • simplest_server.go
    package main
    
    import (
    	"net/http"
    )
    
    func main() {
    	//”“,使用80端口
    	//nil,使用默认多路复用器DefaultServeMux
    	http.ListenAndServe("", nil)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • configurable_server.go
    package main
    
    import (
    	"net/http"
    )
    
    func main() {
    	server := http.Server{
    		Addr:    "127.0.0.1:8080",
    		Handler: nil,
    	}
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    type Server struct {
    	Addr string
    	Handler Handler
    	TLSConfig *tls.Config
    	ReadTimeout time.Duration		//读超时
    	ReadHeaderTimeout time.Duration
    	WriteTimeout time.Duration		//写超时
    	IdleTimeout time.Duration
    	MaxHeaderBytes int
    	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    	ConnState func(net.Conn, ConnState)
    	ErrorLog *log.Logger	//错误日志记录器
    	BaseContext func(net.Listener) context.Context
    	ConnContext func(ctx context.Context, c net.Conn) context.Context
    	inShutdown atomicBool
    	disableKeepAlives int32 
    	nextProtoOnce     sync.Once
    	nextProtoErr      error
    	mu         sync.Mutex
    	listeners  map[*net.Listener]struct{}
    	activeConn map[*conn]struct{}
    	doneChan   chan struct{}
    	onShutdown []func()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.2.2 通过HTTPS提供服务

    密码或信用卡等私密信息,HTTPS对通信进行加密和保护。
    SSL(Secure Socket Layer,安全套接字层),通过公钥基础设施(Public Key Infrastructure,PKI)为通信双方数据加密和身份验证(SSL证书,存储在服务器,包含公钥及相关信息的数据文件)。
    TLS(Transport Layer Security),传输层安全协议。
    HTTPS = HTTP + SSL/TLS

    (Certificate Authority)CA签发SSL证书,保证可靠性。
    (1)客户端发送请求,服务器返回证书和响应;
    (2)客户端确认证书真实性,生成随机密钥(random key);
    (3)利用证书公钥对随机密钥加密,产生实际密钥(actual key,对称密钥symmetric key),负责对通信加密。

    • https_server.go
    package main
    
    import (
    	"net/http"
    )
    
    func main() {
    	server := http.Server{
    		Addr:    "127.0.0.1:8080",
    		Handler: nil,
    	}
    	//cert.pem,SSL证书
    	//key.pem,服务器私钥
    	server.ListenAndServeTLS("cert.pem", "key.pem")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • gencert.go
    package main
    
    import (
    	"crypto/rand"
    	"crypto/rsa"
    	"crypto/x509"
    	"crypto/x509/pkix"
    	"encoding/pem"
    	"math/big"
    	"net"
    	"os"
    	"time"
    )
    
    func main() {
    	max := new(big.Int).Lsh(big.NewInt(1), 128)
    	serialNumber, _ := rand.Int(rand.Reader, max)
    	subject := pkix.Name{//专有名称(distinguished name)
    		Organization:       []string{"Manning Publications Co."},
    		OrganizationalUnit: []string{"Books"},
    		CommonName:         "Go Web Programming",
    	}
    
    	template := x509.Certificate{
    		SerialNumber: serialNumber,//证书序列号,CA分发的唯一号码
    		Subject:      subject,
    		NotBefore:    time.Now(),
    		NotAfter:     time.Now().Add(365 * 24 * time.Hour),//有效期一年
    		//用于服务器身份验证操作
    		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    		IPAddresses:  []net.IP{net.ParseIP("127.0.0.1")},
    	}
    
    	//RSA私钥,里面含有公开访问的公钥(public key)
    	pk, _ := rsa.GenerateKey(rand.Reader, 2048)
    
    	//DER编码格式化的字节切片
    	derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &pk.PublicKey, pk)
    	
    	//证书编码到文件中
    	certOut, _ := os.Create("cert.pem")
    	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    	certOut.Close()
    	
    	//PEM编码将密钥编码到文件中
    	keyOut, _ := os.Create("key.pem")
    	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)})
    	keyOut.Close()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    3.3 处理器和处理器函数

    默认多路复用器未设置处理器时,返回404 HTTP响应。

    3.3.1 处理请求

    //处理器定义
    type Handler interface {
    	ServeHTTP(ResponseWriter, *Request)
    }
    
    • 1
    • 2
    • 3
    • 4

    DefaultServeMux多路复用器是ServeMux(实现了Handler接口)的实例。
    DefaultServeMux也是一个特殊的处理器,将请求URL重定向到不同处理器。

    • handler_server.go
    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    type MyHandler struct{}
    
    func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello World!")
    }
    
    func main() {
    	handler := MyHandler{}
    	server := http.Server{
    		Addr:    "127.0.0.1:8080",
    		Handler: &handler,	//指定处理器
    	}
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.3.2 使用多个处理器

    • multihandler_server.go
    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    type HelloHandler struct{}
    
    func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello!")
    }
    
    type WorldHandler struct{}
    
    func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "World!")
    }
    
    func main() {
    	hello := HelloHandler{}
    	world := WorldHandler{}
    	
    	//未指定处理器,使用DefaultServeMux作为处理器
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    
    	//将处理器绑定至DefaultServeMux
    	/*
    	package http
    	func Handle(pattern string, handler Handler) { 
    		DefaultServeMux.Handle(pattern, handler) 
    	}
    	*/
    	http.Handle("/hello", &hello)
    	http.Handle("/world", &world)
    
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3.3.3 处理器函数

    与处理器拥有相同行为的函数。

    //处理器函数类型定义
    //实现了Handler接口
    type HandlerFunc func(ResponseWriter, *Request)
    func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    	f(w, r)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • handlerfunc_server.go
    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func hello(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello!")
    }
    
    func world(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "World!")
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	/*
    	package http
    	func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    		DefaultServeMux.HandleFunc(pattern, handler)
    	}
    	
    	func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    		if handler == nil {
    			panic("http: nil handler")
    		}
    		mux.Handle(pattern, HandlerFunc(handler))
    	}
    	*/
    	http.HandleFunc("/hello", hello)
    	http.HandleFunc("/world", world)
    
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.3.4 串联处理器和处理器函数

    使用串联(chaining)技术分隔代码中的横切关注点(cross-cutting,日志记录、安全检查和错误处理等操作)。
    chain_handlerfunc.go

    package main
    
    import (
    	"fmt"
    	"net/http"
    	"reflect"
    	"runtime"
    )
    
    func hello(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello!")
    }
    
    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("Handler function called - " + name)
    		h(w, r)
    	}
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/hello", log(hello))
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    串联多个函数让程序执行更多动作,管道处理(pipeline processing)。
    chain_handler.go

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    type HelloHandler struct{}
    
    func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello!")
    }
    
    func log(h http.Handler) http.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) {
    		// some code to make sure the user is authorized
    		h.ServeHTTP(w, r)
    	})
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	hello := HelloHandler{}
    	http.Handle("/hello", protect(log(hello)))
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    3.3.5 ServeMux和DefaultServeMux

    ServeMux,将请求URL重定向(映射)至相应处理器。

    DefaultServeMux是ServeMux的实例,默认多路复用器。
    最小惊讶|意外原则(The Principle of Least Surprise),设计时做合乎常理的事情,使事情的行为总是显而易见、始终如一并且合乎常理,比如门旁边的按钮与门有关。

    (1)URL非/结尾,匹配完全相同的URL;
    (2)URL以/结尾,前缀匹配即可。

    /hello与/hello/不匹配,与/hello,/匹配
    /hello/there与/hello/there/不匹配,与/hello/there,/hello//hello,/匹配
    
    • 1
    • 2

    3.3.6 其它多路复用器

    httprouter.go

    package main
    
    import (
    	"fmt"
    	"github.com/julienschmidt/httprouter"
    	"net/http"
    )
    
    func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    	fmt.Fprintf(w, "hello, %s!\n", p.ByName("name"))
    }
    
    func main() {
    	mux := httprouter.New()
    	//绑定URL的GET方法
    	//:name,具名参数(named parameter)
    	mux.GET("/hello/:name", hello)
    
    	server := http.Server{
    		Addr:    "127.0.0.1:8080",
    		Handler: mux,
    	}
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.4 使用HTTP/2

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    type MyHandler struct{}
    
    func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello World!")
    }
    
    func main() {
    	handler := MyHandler{}
    	server := http.Server{
    		Addr:    "127.0.0.1:8080",
    		Handler: &handler,
    	}
    	//http2.ConfigureServer(&server, &http2.Server{})
    	server.ListenAndServeTLS("cert.pem", "key.pem")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    curl -I --http2 --insecure https://localhost:8080/
    curl --http2 --insecure https://localhost:8080/
    
    • 1
    • 2
    curl --help
    -d	发送数据
    -I, --head	只显示头部	
    -i,包含协议响应头
    --http2 	使用HTTP2
    --insecure	强制接收创建的证书
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    如何用卡片法高效做视频?
    vs2019+qgis+qt学习总结
    CMakeLists编译前拷贝文件或目录
    [微前端实战]---023系统重构
    【笔记】python函数中指定输入和输出格式
    PCIE-Malformed tlp,UR,UC,CA
    RK3399平台开发系列讲解(CPU篇)常见CPU性能问题
    CentOS Nginx crontab 定时执行PHP代码命令任务(tp5.1 tp6 EasyTask) 每天重启执行PHP自定义命令
    AntDB-M高性能设计之hash索引动态rehash
    numpy常用乘法函数总结:np.dot()、np.multiply()、*、np.matmul()、@、np.prod()、np.outer()
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/126705866