Go语言入门——实践篇(五) 青旅半醒 2024-04-18 14:22 80阅读 0赞 ### 文章目录 ### * Web开发基础 * * 最简示例 * Go Web工作流程 * * 创建多路复用器 * 处理静态文件 * 创建处理器函数 * Go Web 应用基础 * * 简单配置 * 处理器与处理器函数 * * 创建处理器 * 创建多个处理器 * 多路复用器 * * 第三方多路复用器 * 欢迎关注我的公众号:编程之路从0到1 # Web开发基础 # 所谓Web开发,也就是我们通常说的网站后端开发。与其他语言相比,Go的Web开发具有简单易学,并发效率高,原生标准库支持等特点。即使是Python Web开发,也没有Go的简单。 学习Go的Web,是可以不需要安装任何第三方库的,标准库即支持,且底层已经使用Go协程封装了并发请求,因此Go不需要任何所谓的服务器容器的软件,例如Java开发需要Tomcat服务器,Python需要Gunicorn,uWSGI之类的服务器,而Go语言,直接上手撸API即可,可以说Go语言是为Web而生的,最适合后端开发。 学习Web开发,应当具备HTTP协议的基础,请先阅读我的另一篇文章 [Web基础(一) HTTP详解][Web_ HTTP] ## 最简示例 ## 1. 运行以下代码 package main import ( "fmt" "net/http" ) //定义一个名为 handler 的处理器函数 func handler(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer, "Hello, %s!", request.URL.Path[1:]) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } 1. 在浏览器输入`http://localhost:8080/Mary` 这时浏览器显示`Hello, Mary!` `handler`函数:事件被触发之后,负责对事件进行处理的回调函数。该处理器函数接受两个参数,第一个参数类型为`ResponseWriter`接口 , 第二个参数为指向`Request`结构的指针。 `handler`函数会从 Request 结构中提取相关的信息,然后创建一个HTTP响应, 最后再通过`ResponseWriter`接口将响应返回给客户端。 `handler`函数中的`Fprintf`函数在被调用时,需要传入一个`ResponseWriter`接口实例 ,第二个参数是带有格式化占位符`%s`的字符串,第三参数就是占位符需要替换的内容,这里则是将`Request`结构里带的URL路径截取后作为参数 ## Go Web工作流程 ## ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70] 示例代码 package main import ( "net/http" "time" "fmt" ) func main() { fmt.Println("*** 服务器启动,监听端口:8080 ***") mux := http.NewServeMux() // 处理静态资源文件 files := http.FileServer(http.Dir("./public")) mux.Handle("/static/", http.StripPrefix("/static/", files)) mux.HandleFunc("/", index) // 配置服务器 server := &http.Server{ Addr: "0.0.0.0:8080", Handler: mux, // 设置多路复用器 ReadTimeout: time.Duration(10 * int64(time.Second)), WriteTimeout: time.Duration(200 * int64(time.Second)), MaxHeaderBytes: 1 << 20, // 左移运算,等同:1*2^20,高性能乘法运算 } server.ListenAndServe() } func index(writer http.ResponseWriter, request *http.Request) { fmt.Fprintln(writer, "Hello, world!") } 1. 创建以上代码 2. 在代码所在目录创建`public`文件夹 3. 在`public`中分别创建`home.html`、`note.txt`文件 4. 使用编辑器打开创建的两个文件,分别将以下内容复制粘贴到文件中保存 <html> <head> <title> 这是主页 </title> </head> <body style="background:Pink"> <h1>Home</h1> <p style="color:red"> Welcome to the Go homepage! </p> </body> </html> 现在,让我们在浏览器分别访问`http://127.0.0.1:8080/`、`http://127.0.0.1:8080/static/home.html`和`http://127.0.0.1:8080/static/note.txt` ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 1] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 2] 可以发现,`http://127.0.0.1:8080/static/home.html`和`http://127.0.0.1:8080/static/note.txt`的结果是不一样的,这是因为浏览器能识别`html`标记语言,关于前端`html`标记语言本文不做说明,请自行学习前端相关知识。 ### 创建多路复用器 ### > 通过`NewServeMux`函数来创建一个默认的多路复用器,调用`HandleFunc`函数将发送至根URL的请求重定向到对应的处理器。因为所有处理器都接受一个 `ResponseWriter` 实例和一个指向 `Request` 结构的指针作为参数,并且所有请求参数都可以通过访问 `Request` 结构得到,所以程序并不需要向处理器显式地传入任何请求参数。 多路复用器主要负责接收 HTTP 请求,并根据请求中的 URL 将请求重定向到正确的处理器。 注意,所有引入了 `net/http` 标准库的程序都可以使用一个默认的多路复用器实例,当没有为 `Server` 结构指定处理器时,服务器就会使用 `DefaultServeMux` 。 实际上,所谓多路复用器,也就是我们在开发中常说的路由的概念,根据不同的URL,调用不同的函数去处理。 ### 处理静态文件 ### > 使用`FileServer`函数创建了一个处理器,它能够处理指定目录中的静态文件。最后将这个处理器传递给多路复用器的`Handle`函数 如示例代码,当服务器接收到一个以`/static/`开头的 URL 请求时,以上将URL中的`/static/`路径映射到`public`目录中,然后查找被请求的文件。 例如,当服务器接收到一个针对文件 `http://127.0.0.1:8080/static/note.txt`的请求时,它将会在`public`目录中查找`note.txt`文件。这样做的好处是可以将服务器上的真实文件目录隐藏。 ### 创建处理器函数 ### > 处理器函数实际上就是一个接受`ResponseWriter`和`Request`指针作为参数的 Go 函数。通常该函数负责生成HTML内容并将其写人`ResponseWriter`中。 func index(writer http.ResponseWriter, request *http.Request) { fmt.Fprintln(writer, "Hello, world!") } ## Go Web 应用基础 ## ### 简单配置 ### > 除了可以通过`ListenAndServe`的参数对服务器的网络地址和处理器进行配置之外,还可以通过 Server 结构对服务器进行更详细的配置,如上面的例子,其中包括为请求读取操作设置超时时间、为响应写入操作设置超时时间,以及为 Server 结构设置错误日志记录器等 最简配置 package main import ( "net/http" ) func main() { server := http.Server{ Addr : "127.0.0.1:8080" , Handler : nil, } server.ListenAndServe() } `Server`结构详细字段 type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler // handler to invoke, http.DefaultServeMux if nil TLSConfig *tls.Config // optional TLS config, used by ServeTLS and ListenAndServeTLS 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 disableKeepAlives int32 // accessed atomically. inShutdown int32 // accessed atomically (non-zero means we're in Shutdown) nextProtoOnce sync.Once // guards setupHTTP2_* init nextProtoErr error // result of http2.ConfigureServer if used mu sync.Mutex listeners map[net.Listener]struct{ } activeConn map[*conn]struct{ } doneChan chan struct{ } onShutdown []func() } ### 处理器与处理器函数 ### > **处理器** 是一个拥有`ServeHTTP`方法的接口,这个`ServeHTTP`方法需要接受两个参数,第一个参数是一个`ResponseWriter`接口实例,而第二个参数则是一个指向`Request`结构的指针。即任何拥有`ServeHTTP`方法的接口,且该方法的签名为`ServeHTTP(http.ResponseWriter, *http.Request)`,那么这个接口就是一个处理器。 > > **处理器函数** 实际上是与处理器拥有相同行为的函数,这个函数与`ServeHTTP`方法拥有相同的签名,即接受`ResponseWriter`和指向`Request`结构的指针作为参数。通过使用`HandlerFunc`可以把一个带有正确签名的函数`f`转换成一个带有方法 `f`的处理器实例,这个方法会与`DefaultServeMux`进行绑定。因此,处理器函数只不过是创建处理器的一种便利方法而已。 虽然**处理器函数**能够完成跟**处理器**一样的工作,并且使用处理器函数的代码比使用处理器的代码更为简洁,但是处理器函数并不能完全代替处理器。 这是因为在某些情况下,代码可能已经包含了某个接口或者某种类型,这时我们只需要为它们添加 `ServeHTTP`方法就可以将它们转变为处理器,这种转变更灵活也有助于构建出更为模块化的 Web 应用。 #### 创建处理器 #### package main import ( "fmt" "net/http" ) // 声明一个结构体 type MyHandler struct{ } // 让结构体实现 ServeHTTP 函数 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() } #### 创建多个处理器 #### > 不用在`Server`结构的`Handler`字段中指定处理器,而是让服务器使用默认的 `DefaultServeMux`作为处理器, 然后通过`http.Handle`函数将处理器绑定至 `DefaultServeMux`。 需要说明的是,虽然`Handle`函数来源于`http`包,但它实际上是`ServeMux`结构的方法,这些函数是为了操作便利而创建的函数,调用它们等同于调用`DefaultServeMux`的方法。 比如调用`http.Handle`实际上就是在调用`DefaultServeMux`的`Handle`方法 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{ } server := http.Server{ Addr: "127.0.0.1:8080", } http.Handle("/hello", &hello) http.Handle("/world", &world) server.ListenAndServe() } ### 多路复用器 ### > `ServeMux`是一个HTTP请求多路复用器,它负责接收HTTP请求并根据请求中的 URL将请求重定向到正确的处理器。`ServeMux`结构包含了一个映射,这个映射会将URL映射至相应的处理器。因为`ServeMux`结构也实现了`ServeHTTP`方法,所以它也是一个处理器 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 3] `ServeMux`是一个结构体而不是一个接口,因此`DefaultServeMux`是`ServeMux`的一个实例而不`ServeMux`的实现。 前面提到的`http.ListenAndServe(":8080", nil)`函数接受的第二个参数是一个处理器,但它的默认值却是一个多路复用器`DefaultServeMux`,这是因为`DefaultServeMux`多路复用器是`ServeMux`结构的一个实例,而`ServeMux`也拥有`ServeHTTP`方法,也就是说`DefaultServeMux`既是`ServeMux`结构的实例,也是`Handler`结构的实例。 #### 第三方多路复用器 #### > `ServeMux`的一个缺陷是无法使用变量实现URL模式匹配。例如在浏览器请求`/threads`的时候,`ServeMux`可以很好地获取并显示所有帖子,但当浏览器请求的是`/threads/123`时,那么要获取并显示ID为123的帖子就会变得非常困难。 创建自定义的多路复用器来代替`net/http`包中的`ServeMux`是可行的,并且目前市面上已经出现了很多第三方的多路复用器可供使用,而`HttpRouter`就是一个功能强大的轻量级第三方多路复用器。 执行以下命令安装 HttpRouter go get -u github.com/julienschmidt/httprouter 这表示从GitHub上下载HttpRouter包源码,并将其保存到GOPATH/src目录中 使用示例 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() //将处理器函数与给定的HTTP方法进行绑定 mux.GET("/hello/:name", hello) server := http.Server{ Addr: "127.0.0.1:8080", Handler: mux, } server.ListenAndServe() } 以上代码不再使用`HandleFunc`绑定处理器函数,而是直接把处理器函数与给定的 HTTP方法进行绑定。当浏览器向这个URL发送GET请求时,`hello`函数就会被调用,但当浏览器向这个URL发送其他请求时,`hello`函数不会被调用。 可以看到被绑定的URL包含了具名参数(named parameter),这些具名参数会被 URL中的具体值所代替,并且程序可以在处理器里面获取这些值。此时的处理器函数`hello`接收三个参数,而第三个参数`Params`就包含了具名参数,其值可以 通过`ByName`方法获取。 如,运行程序后,浏览器输入`localhost:8080/hello/fox`,则显示 **hello,fox**,`p.ByName`成功获取到URL中的fox字段。 # 欢迎关注我的公众号:编程之路从0到1 # ![编程之路从0到1][0_1] [Web_ HTTP]: https://blog.csdn.net/yingshukun/article/details/83863152 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/18/ffd20380692648058688bfad25e277dd.jpg [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 1]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/18/741bcac54f7148a4a912925bc910fe4d.jpg [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 2]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/18/03ec1952b20848e3abd0fbade40c1448.jpg [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9hcmN0aWNmb3guYmxvZy5jc2RuLm5ldA_size_16_color_FFFFFF_t_70 3]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/18/bd6ae833fe984aa982d63852733ea328.jpg [0_1]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/18/88944267f3944cccacbd9205911b55aa.jpg
相关 C语言入门篇(五) 现实生活中,库函数并不能解决所有问题,于是就有了自定义函数。自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是不一样的是这些都是我们自己来设计。statemen... 快来打我*/ 2024年04月23日 20:24/ 0 赞/ 76 阅读
相关 Go语言实践篇之MongoDB 文章目录 * Go语言中MongoDB的使用 * * 环境准备 * * 安装 * 快速上手 * CRUD操作 ... 喜欢ヅ旅行/ 2024年04月19日 08:59/ 0 赞/ 80 阅读
相关 Go语言入门——实践篇(五) 文章目录 * Web开发基础 * * 最简示例 * Go Web工作流程 * * 创建多路复用器 * 处理... 青旅半醒/ 2024年04月18日 14:22/ 0 赞/ 81 阅读
相关 Go语言入门 Go语言入门 简介 Go是一门由Google开发的开源编程语言,旨在提供高效、可靠和简洁的软件开发工具。Go具有静态类型、垃圾回收、并发性和高效编译的特点,适用于构 Myth丶恋晨/ 2023年10月14日 19:52/ 0 赞/ 75 阅读
相关 【Go】Go 语言教程--语言变量(五) 往期教程: [Go 语言教程–介绍(一)][Go] [Go 语言教程–语言结构(二)][Go 1] [Go 语言教程–语言结构(三)][Go 2] [ 待我称王封你为后i/ 2023年10月13日 11:33/ 0 赞/ 40 阅读
相关 go语言入门 一、Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、数组安全、编译迅速 二、语言用途 Go 语言被设计成一门应用于搭载 Web 服务器,存 矫情吗;*/ 2022年12月27日 11:13/ 0 赞/ 248 阅读
相关 Go语言入门 在学习Go语言编程之前,我们需要安装和配置好Go语言的开发环境。可以选择线上的编译器:[http://tour.golang.org/welcome/1][http_tour. 缺乏、安全感/ 2022年06月01日 08:52/ 0 赞/ 303 阅读
相关 Go语言入门——环境准备篇(一) 文章目录 环境准备篇 背景 Go开发环境搭建 下载 环境配置 IDE的选择 客官°小女子只卖身不卖艺/ 2021年11月17日 00:52/ 0 赞/ 398 阅读
还没有评论,来说两句吧...