当前位置: 首页 > article >正文

【Gee】Day1:HTTP 基础

Day1:HTTP 基础

今天的任务是:

  • 简单介绍net/http库以及http.Handler接口;
  • 搭建Gee框架的雏形,代码约 50 行;

标准库启动 Web 服务

Golang 内置了 net/http 库,封装了 HTTP 网络编程基础的接口,我们将要实现的 Gee 框架就是基于 net/http 的。接下来通过一个例子介绍 net/http 这个库如何使用:

package main

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

func main() {
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/hello", helloHandler)
	log.Fatal(http.ListenAndServe(":9999", nil))
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
}

func helloHandler(w http.ResponseWriter, req *http.Request) {
	for k, v := range req.Header {
		fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
	}
}

在上例当中我们设置了 2 个路由,分别是//hello,并将它们分别与 indexHandler 和 helloHandler 绑定,这两个函数会根据不同的 HTTP 请求调用不同的处理函数。访问 /,响应是 URL.Path = /。而 /hello 的响应则是 header 中的键值对信息。

main 函数的最后一行是用来启动 Web 服务的,第一个参数是地址,:9999代表在localhost:9999监听。第二个参数代表处理所有的 HTTP 请求的实例,nil 代表使用标准库中的实例处理。

第二个参数正是我们基于 net/http 标准库实现 Web 框架的入口。

实现 http.Handler 接口

package http

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

func ListenAndServe(address string, h Handler) error

通过查看net/http的源码,发现Handler是一个接口,需要实现ServeHTTP方法,即:只要传入任何实现了 ServeHTTP 接口的实例,所有的 HTTP 请求就都将会交给这个传入的实例来处理了。现在让我们尝试一下:

package main

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

type Engine struct{}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.URL.Path {
	case "/":
		fmt.Fprint(w, "URL.Path = %q\n", req.URL.Path)
	case "/hello":
		for k, v := range req.Header {
			fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
		}
	default:
		fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL)
	}
}

func main() {
	engine := new(Engine)
	log.Fatal(http.ListenAndServe(":9999", engine))
}

  • 在上例当中,我们定义了一个空结构体Engine,它实现了方法ServeHTTP。这个方法有两个参数,第二个参数是 Request,该对象包含了该 HTTP 请求的所有的信息,比如请求地址、Header 和 Body 等信息;第一个参数是 ResponseWriter,利用 ResponseWriter 可以构造针对该请求的响应。
  • 在 main 当中,我们给 ListenAndServe 方法的第二个参数传入了刚才创建的 engine 实例。至此,我们已经走出了实现 Web 框架的第一步,即:将所有的 HTTP 请求转向了我们自己的处理逻辑。在实现 Engine 之前,我们只能通过调用 http.HandleFunc 才能实现路由和 Handler 映射,也就是只能针对具体的路由写处理逻辑。但是在实现 Engine 之后,我们拦截了所有的 HTTP 请求,拥有了统一的控制入口。在 Engine 的 ServeHTTP 方法当中,我们可以自由定义路由映射的规则,也可以统一添加一些处理逻辑,例如日志、异常处理等。

Gee 框架的雏形

文件的组织形式如下:
在这里插入图片描述

首先“搭建” main 函数:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	r := gee.New()
	r.GET("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
	})
	
	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)
		}
	})
	
	r.Run(":9999")
}

gee 在设计时其框架和 API 的设计均参考了 gin,因此上述逻辑与 gin 非常相似。使用 New() 创建 gee 的服务实例,使用 GET() 方法添加路由,最后使用 Run() 启动 Web 服务。目前路由仅支持静态路由。

gee.go

我们再来实现 gee.go

package gee

import (
	"fmt"
	"net/http"
)

// HandlerFunc defines the request handler used by gee
type HandlerFunc func(w http.ResponseWriter, r *http.Request)

// Engine implements the interface of ServeHTTP
type Engine struct {
	router map[string]HandlerFunc
}

// New is the constructor of gee.Engine
func New() *Engine {
	return &Engine{router: make(map[string]HandlerFunc)}
}

// addRoute combines method and pattern together and then add method-pattern and handler to map
func (engine *Engine) addRoute(method, pattern string, handler HandlerFunc) {
	key := method + "-" + pattern
	engine.router[key] = handler
}

// GET defines the method to add GET request
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
	engine.addRoute("GET", pattern, handler)
}

// POST defines the method to add POST request
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
	engine.addRoute("POST", pattern, handler)
}

func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	key := req.Method + "-" + req.URL.Path // method + "-" + pattern, as key in Engine's map
	if handler, ok := engine.router[key]; ok {
		handler(w, req)
	} else {
		fmt.Fprintf(w, "404 NOT FOUNT: %s\n", req.URL)
	}
}

  • 首先定义了类型HandlerFunc,它是提供给框架用户的,用来定义路由映射的处理方法。在Engine中,添加了一张路由映射表routerkey由请求方法和静态路由地址拼接而成,例如:GET-/GET-/helloPOST-/hello等。
  • 当用户调用(*Engine).GET()方法时,会将路由和处理方法注册到映射表 router 中。(*Engine).Run()是 ListenAndServe 的包装。
  • Engine 实现的 ServeHTTP 方法的作用就是,解析请求的路径,查找路由映射表,如果查到了,那么就执行注册的处理方法,否则返回 404 NOT FOUND。

至此,Gee 框架的原型已经完成了,逻辑还是非常的清晰和易于理解的。


http://www.kler.cn/a/560909.html

相关文章:

  • 深入讲解微信小程序 <canvas> 标签的 type=“2d“属性
  • 漏洞文字版表述一句话版本(漏洞危害以及修复建议),通常用于漏洞通报中简洁干练【持续更新中】
  • 【Java项目】基于SpringBoot的【旅游管理系统】
  • Python爬虫系列教程之第十五篇:爬取电商网站商品信息与数据分析
  • C++ QT 6.6.1 QCustomPlot的导入及使用注意事项和示例 | 关于高版本QT使用QCustomPlot报错问题解决的办法
  • SSL和TLS:深入了解网络安全的基石
  • 【DeepSeek与鸿蒙HarmonyOS:开启应用开发新次元】
  • DSP芯片C6678的SRIO及其中断跳转的配置
  • LeetCode 热题 100 94. 二叉树的中序遍历
  • 基于SpringBoot的“流浪动物救助系统”的设计与实现(源码+数据库+文档+PPT)
  • Redis中集合(Set)常见命令详解
  • MySQL 主从集群同步延迟问题分析与解决方案
  • Transformer LLaMA
  • Qt在Linux嵌入式开发过程中复杂界面滑动时卡顿掉帧问题分析及解决方案
  • 部署若依微服务遇到的坑
  • 被AWS反撸了,试一下能否申请退还
  • AWS EC2加速型计算实例全解析:从vt1到p5,如何为AI算力选择最佳引擎?
  • qt:多元素类,容器类,布局类
  • 基于Docker的前端环境管理:从开发环境到生产部署的实现方案
  • Rancher-产品架构