Go语言入门——实践篇(五)

Web开发基础

所谓Web开发,也就是我们通常说的网站后端开发。与其他语言相比,Go的Web开发具有简单易学,并发效率高,原生标准库支持等特点。即使是Python Web开发,也没有Go的简单。

学习Go的Web,是可以不需要安装任何第三方库的,标准库即支持,且底层已经使用Go协程封装了并发请求,因此Go不需要任何所谓的服务器容器的软件,例如Java开发需要Tomcat服务器,Python需要Gunicorn,uWSGI之类的服务器,而Go语言,直接上手撸API即可,可以说Go语言是为Web而生的,最适合后端开发。

学习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工作流程

在这里插入图片描述

示例代码

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.htmlnote.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.htmlhttp://127.0.0.1:8080/static/note.txt
在这里插入图片描述
在这里插入图片描述
可以发现,http://127.0.0.1:8080/static/home.htmlhttp://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文件。这样做的好处是可以将服务器上的真实文件目录隐藏。

创建处理器函数

处理器函数实际上就是一个接受ResponseWriterRequest指针作为参数的 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实际上就是在调用DefaultServeMuxHandle方法

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方法,所以它也是一个处理器

在这里插入图片描述

ServeMux是一个结构体而不是一个接口,因此DefaultServeMuxServeMux的一个实例而不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,foxp.ByName成功获取到URL中的fox字段。

欢迎关注我的公众号:编程之路从0到1

编程之路从0到1

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值