【极客兔兔-Web框架Gee详解】Day1环境搭建与前置知识


一、环境搭建

以Goland举例

1. 创建项目

在这里插入图片描述
在这里插入图片描述

【Q】 创建时go和go(gopath)的区别?

这两者的区别主要在于Go的模块管理方式。

  1. Go Modules(Go)
  • 简介:Go Modules是Go语言的现代依赖管理系统,从Go 1.11开始引入,并在Go 1.13中成为默认的依赖管理方式。
  • 特点:
    • 模块化管理:使用go.mod文件来管理项目的依赖关系。
    • 版本控制:支持版本化的依赖管理,可以指定依赖的版本。
    • 不依赖GOPATH:项目可以在任何目录下创建,不需要放在GOPATH中。
    • 更灵活:适合现代软件开发的需求,特别是对于多模块项目。
  1. GOPATH(Go (GOPATH))
  • 简介:GOPATH是Go语言的传统工作区模式,在Go Modules出现之前是唯一的依赖管理方式。
  • 特点:
    • 固定目录结构:所有的Go代码必须放在GOPATH/src目录下。
    • 全局依赖管理:所有项目共享同一个GOPATH,依赖管理不够灵活。
    • 不支持版本控制:没有内置的版本管理机制,依赖管理较为原始。
    • 逐渐被淘汰:随着Go Modules的普及,GOPATH模式逐渐被淘汰。

选择哪个?
Go Modules(Go):如果是Go 1.11及以后,推荐使用Go Modules,因为它提供了更现代化的依赖管理方式,支持版本控制,并且不依赖于GOPATH的固定目录结构。
GOPATH(Go (GOPATH)):仅在需要维护旧项目或特定情况下使用。

2. 按天创建包

在这里插入图片描述

  • 给包取名:day1-http-base
    在这里插入图片描述
  • 第一天有三个示例需要展示
    在这里插入图片描述
    • 分别取名base1, base2, base3 如下
      在这里插入图片描述

二、标准库net/http以及http.Handler接口

在这里插入图片描述
在这里插入图片描述

1. 开敲代码,看看效果

Go语言内置了 net/http库,封装了HTTP网络编程的基础的接口,Gee Web 框架便是基于net/http的。我们接下来通过一个例子,简单介绍下这个库的使用。

1.1 代码

文件结构:day1-http-base/base1/main.go

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", indexHandler) // 注册根路径的处理函数
	http.HandleFunc("/hello", helloHandler) // 注册/hello路径的处理函数
	log.Fatal(http.ListenAndServe(":9999", nil)) // 启动HTTP服务器,监听9999端口
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path) // 输出请求的URL路径
}

func helloHandler(w http.ResponseWriter, req *http.Request) {
	for k, v := range req.Header { // 遍历请求头
		fmt.Fprintf(w, "Header[%q] = %q\n", k, v) // 输出每个请求头的键值对
	}
}

1.2 测试及运行结果

1.2.1 测试工具

采用curl这个工具,进行测试。Curl 是一个命令行工具和库,用于通过URL传输数据。简单来说, Curl 就是一个可以通过命令行发送GET,POST 等多种协议请求的工具。curl 是 http 调试必备的一个命令行工具

Windows 10 中内置了 curl 工具。若没有,需要手动安装。以windows为例

  1. 去官网下载.zip文件
    链接: curl官网
    在这里插入图片描述
  2. 解压
  3. 配置环境变量
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4. 验证
Win + R 输入cmd打开命令行
输入curl --version,得到版本信息即安装成功
在这里插入图片描述

1.2.2 运行

在这里插入图片描述
在Terminal中输入命令,得到下边的运行结果,即运行成功

$ curl http://localhost:9999/
URL.Path = "/"
$ curl http://localhost:9999/hello
Header["Accept"] = ["*/*"]
Header["User-Agent"] = ["curl/7.54.0"]

2. 对着代码解释一下

2.1 整体解释一下

  1. main函数:
  • 使用http.HandleFunc为根路径//hello路径注册了两个处理函数indexHandlerhelloHandler
  • 使用http.ListenAndServe启动HTTP服务器,监听本地的9999端口。如果服务器启动失败,log.Fatal会记录错误并终止程序。
  1. indexHandler函数:
  • 处理根路径/的请求。
  • 使用fmt.Fprintf将请求的URL路径写入响应中。
  1. helloHandler函数:
  • 处理/hello路径的请求。
  • 遍历请求的头部信息,并将每个头部的键值对写入响应中。

这个程序的功能是启动一个简单的HTTP服务器,能够响应两个不同路径的请求,并在响应中输出请求的相关信息。

2.2 请求头是什么,具体什么格式?

在HTTP协议中,请求头(Request Headers)是客户端发送给服务器的附加信息,用于提供关于客户端环境、请求的细节以及客户端期望的响应格式等信息。请求头以键值对的形式存在,每个请求头占一行,键和值之间用冒号分隔。

  1. 请求头的格式

Header-Name: Header-Value

  1. 常见的请求头
  • Host: 指定请求的主机名和端口号。
    示例:Host: www.example.com
  • User-Agent: 提供关于客户端软件的信息。
    示例:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
  • Accept: 指定客户端能够处理的内容类型。
    示例:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
  • Accept-Language: 指定客户端能够理解的自然语言。
    示例:Accept-Language: en-US,en;q=0.5
  • Accept-Encoding: 指定客户端能够理解的内容编码。
    示例:Accept-Encoding: gzip, deflate, br
  • Connection: 控制连接的选项。
    示例:Connection: keep-alive
  • Cookie: 发送存储在客户端的cookie。
    示例:Cookie: sessionId=abc123
  1. 以下是一个完整的HTTP请求头示例:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: sessionId=abc123

2.3 w http.ResponseWriter, req *http.Request 这两个是什么类型

这两个参数是Go语言中处理HTTP请求的核心部分,ResponseWriter用于构建和发送响应,而Request用于读取和解析请求。
http请求就记住:!!根据请求,构建相应!!

  1. http.ResponseWriter:
  • 这是一个接口,提供了构建HTTP响应的方法。
  • 通过ResponseWriter,你可以设置HTTP响应的头部信息、状态码,并将响应数据写入客户端。
  • 常用的方法包括:
    • Write([]byte) (int, error): 将数据写入响应体。
    • WriteHeader(statusCode int): 设置HTTP状态码。
    • Header() http.Header: 返回一个Header对象,用于设置响应头。
  1. *http.Request:
  • 这是一个结构体指针,表示HTTP请求的详细信息。
  • 包含了请求的各种信息,如请求方法、URL、头部、主体等。
  • 常用的字段和方法包括:
    • Method string: 请求的方法(如GET, POST)。
    • URL *url.URL: 请求的URL。
    • Header Header: 请求的头部信息。
    • Body io.ReadCloser: 请求的主体。
    • FormValue(key string) string: 获取表单参数的值。

2.4 %q是什么

在Go语言中,%q是fmt包中格式化字符串的一种动词,用于格式化输出带引号的字符串或字符。具体来说:

  • 字符串:当用于字符串时,%q会将字符串用双引号括起来,并对其中的特殊字符进行转义。
  • 字符:当用于字符时,%q会将字符用单引号括起来,并对其中的特殊字符进行转义。
package main

import (
	"fmt"
)

func main() {
	str := "Hello, World!"
	char := 'A'

	fmt.Printf("String: %q\n", str) // 输出: String: "Hello, World!"
	fmt.Printf("Character: %q\n", char) // 输出: Character: 'A'
}

三、在二的基础上抽象

main 函数的最后一行,是用来启动 Web 服务的,第一个参数是地址,:9999表示在 9999 端口监听。而第二个参数则代表处理所有的HTTP请求的实例,nil 代表使用标准库中的实例处理。第二个参数,是基于net/http标准库实现Web框架的入口。

第二个参数的类型是什么呢?通过查看net/http的源码(如下)可以发现,Handler是一个接口,需要实现方法 ServeHTTP ,也就是说,只要传入任何实现了 ServerHTTP 接口的实例,所有的HTTP请求,就都交给了该实例处理了。

package http

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

func ListenAndServe(address string, h Handler) error

1. 开敲代码,瞅瞅结果

文件结构:day1-http-base/base2/main.go
记得创建新包base2,该包下创建main.go
因为一个包只能有一个main.go

1.1 代码

package main

import (
	"fmt"
	"log"
	"net/http"
)

// 定义一个空的结构体类型 Engine
type Engine struct{}

// 实现 http.Handler 接口的 ServeHTTP 方法
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.URL.Path {
	case "/":
		// 如果请求路径是根路径 "/", 返回请求的 URL 路径
		fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
	case "/hello":
		// 如果请求路径是 "/hello",遍历请求头部信息并写入响应
		for k, v := range req.Header {
			fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
		}
	default:
		// 对于其他路径,返回 404 错误信息
		fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL)
	}
}

func main() {
	// 创建一个 Engine 实例
	engine := new(Engine)
	// 启动 HTTP 服务器,监听 9999 端口,并使用 engine 处理请求
	log.Fatal(http.ListenAndServe(":9999", engine))
}

1.2 瞅瞅结果

先运行main, 再curl请求,得到同样结果

$ curl http://localhost:9999/
URL.Path = "/"
$ curl http://localhost:9999/hello
Header["Accept"] = ["*/*"]
Header["User-Agent"] = ["curl/7.54.0"]

2. 详细谈谈

在这段代码中,我们定义了一个名为Engine的空结构体,并为它实现了一个名为ServeHTTP的方法。这个方法有两个参数:

  1. ResponseWriter:这是第一个参数,用于构建和发送HTTP响应。你可以通过它来设置响应的内容、状态码等。
  2. Request:这是第二个参数,包含了HTTP请求的所有信息,比如请求的URL、头部信息(Header)、请求体(Body)等。

main函数中,我们使用http.ListenAndServe启动了一个HTTP服务器,并将我们创建的engine实例作为第二个参数传递给它。这意味着所有的HTTP请求都会被转发到我们的Engine实例进行处理。

2.1 为什么这样做?

在没有实现Engine之前,我们通常使用http.HandleFunc来为每个具体的路由(比如/hello)定义处理逻辑。==这种方式虽然简单,但每个路由都需要单独定义处理函数,缺乏统一的控制。通过实现Engine,我们创建了一个统一的入口来处理所有的HTTP请求。==这就像在门口设置了一个总控人员,所有的请求都要先经过他。这样,我们可以在一个地方定义路由规则,也可以在这里添加一些通用的处理逻辑,比如日志记录、错误处理等。

四、Gee框架的雏形

重新组织二、三的代码,搭建出整个框架的雏形。
最终的代码目录结构是这样的。

├─base1
├─base2
└─base3
    └─gee
        |--gee.go
 		|--go.mod
	  main.go
	  go.mod

1. 开敲代码,瞅瞅结果

1.1 文件结构

  1. 在base3中新建包gee(我们的Gee框架)
  2. 初始化gee模块:打开Terminal ,进入day1-http-base/base3/gee文件夹,初始化gee模块 go mod init gee
    在这里插入图片描述
    初始化成功后,文件结构变成
    在这里插入图片描述
    此时gee是一个模块,模块名为gee(!!记得,接下来还要创建其他模块,不要搞混!!模块名和文件夹名是没有联系的,可以不一样
  3. 初始化example模块: Terminal ,进入day1-http-base/base3文件夹,初始化example模块 go mod init example
    在这里插入图片描述
    结果
    在这里插入图片描述
    此时框架的模块已经建好。

1.2 代码

1.2.1 day1-http-base/base3/go.mod(看好是哪个mod文件,不要搞错文件路径)
module example

go 1.13

require gee v0.0.0 //添加

replace gee => ./gee //添加
  • 在 go.mod 中使用 replace 将 gee 指向 ./gee

从 go 1.11 版本开始,引用相对路径的 package 需要使用上述方式。

【Q】若是显示依赖失败,可以看下Goland是否打开模块化
File–>Settings–>Go -->Go Modules
在这里插入图片描述

1.2.2 day1-http-base/base3/gee/gee.go
package gee

import (
	"fmt"
	"net/http"
)

// 定义一个处理函数类型,接收http.ResponseWriter和*http.Request作为参数
type HandlerFunc func(http.ResponseWriter, *http.Request)

// Engine结构体,包含一个路由映射表
type Engine struct {
	router map[string]HandlerFunc
}

// New函数,创建并返回一个新的Engine实例
func New() *Engine {
	return &Engine{router: make(map[string]HandlerFunc)}
}

// addRoute方法,向路由映射表中添加路由
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
	key := method + "-" + pattern // 生成路由的唯一键
	engine.router[key] = handler  // 将处理函数与路由键关联
}

// GET方法,注册GET请求的路由
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
	engine.addRoute("GET", pattern, handler)
}

// POST方法,注册POST请求的路由
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
	engine.addRoute("POST", pattern, handler)
}

// Run方法,启动HTTP服务器,监听指定的地址
func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}

// ServeHTTP方法,实现http.Handler接口,处理所有的HTTP请求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	key := req.Method + "-" + req.URL.Path // 根据请求方法和路径生成路由键
	if handler, ok := engine.router[key]; ok {
		handler(w, req) // 如果找到处理函数,调用它
	} else {
		fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL) // 否则返回404错误
	}
}

这就是Gee框架的核心代码。

  1. 定义处理函数类型
  • HandlerFunc:这是一个函数类型,用于定义如何处理HTTP请求。开发者可以使用这个类型来编写自己的请求处理逻辑。
  1. 路由映射表
  • router:在Engine结构体中,我们创建了一个路由映射表。这个表的作用是将请求的路径和方法(如GET或POST)与相应的处理函数关联起来。
  • 键的构成:键由请求方法和路径组成,比如GET-/GET-/helloPOST-/hello。这样,即使路径相同,只要请求方法不同,也可以映射到不同的处理函数。
  1. 注册路由
  • GETPOST方法:这些方法允许开发者将特定的路径和处理函数注册到路由映射表中。比如,当你调用engine.GET("/hello", handler)时,框架会将/hello路径与handler函数关联起来。
  1. 启动服务器
  • Run方法:这个方法是对http.ListenAndServe的简单包装,用于启动HTTP服务器并监听指定的地址。
  1. 处理请求
  • ServeHTTP方法:这是框架的核心功能。每当服务器接收到请求时,这个方法会被调用。
    • 路径解析:首先,它会解析请求的路径和方法,生成一个键。
    • 查找路由:然后,它会在路由映射表中查找这个键。
    • 执行处理函数:如果找到对应的处理函数,就执行它。
    • 返回404:如果找不到,就返回一个简单的404错误信息。
1.2.3 day1-http-base/base3/main.go
package main

import (
	"fmt"
	"gee"      // 导入自定义的 gee 包
	"net/http" // 导入 net/http 包用于处理 HTTP 请求和响应
)

func main() {
	r := gee.New() // 创建一个新的 gee.Engine 实例

	// 注册一个处理函数,处理根路径 "/" 的 GET 请求
	r.GET("/", func(w http.ResponseWriter, req *http.Request) {
		// 将请求的 URL 路径写入响应
		fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
	})

	// 注册一个处理函数,处理 "/hello" 路径的 GET 请求
	r.GET("/hello", func(w http.ResponseWriter, req *http.Request) {
		// 遍历请求头部信息,并将每个键值对写入响应
		for k, v := range req.Header {
			fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
		}
	})

	// 启动 HTTP 服务器,监听 9999 端口
	r.Run(":9999")
}

这就是Gee框架的使用。
如果你使用过gin框架的话,肯定会觉得无比的亲切。gee框架的设计以及API均参考了gin。使用New()创建 gee 的实例,使用 GET()方法添加路由,最后使用Run()启动Web服务。这里的路由,只是静态路由,不支持/hello/:name这样的动态路由,动态路由我们将在下一次实现。

1.3 运行

执行go run main.go,再用 curl 工具访问,结果与最开始的一致。

$ curl http://localhost:9999/
URL.Path = "/"
$ curl http://localhost:9999/hello
Header["Accept"] = ["*/*"]
Header["User-Agent"] = ["curl/7.54.0"]
curl http://localhost:9999/world
404 NOT FOUND: /world

至此,整个Gee框架的原型已经出来了。实现了路由映射表,提供了用户注册静态路由的方法,包装了启动服务的函数。当然,到目前为止,我们还没有实现比net/http标准库更强大的能力,不用担心,很快就可以将动态路由、中间件等功能添加上去了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

再坚持一下嘤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值