1. 回顾
我们之前的实现,已经能够方便创建一个HTTP服务器了,唯一需要的是实现Handler接口,完成请求和处理逻辑的映射。能够将请求和对应的处理逻辑映射起来。
我们查看SimpleWeb其实完成了两部分的功能,一部分是http请求和处理函数的映射规则,我们之后称之为路由映射规则,另一部分则是具体的业务处理逻辑。
type SimpleWeb struct {
}
func (s *SimpleWeb) ServeHttp(c *Conn, req *Request) {
// if req.uri == xxx: 这个是路由映射规则
if req.uri == "/hello" {
// 这个是具体的业务处理逻辑
c.WriteData([]byte("hello"))
} else {
c.WriteData([]byte("hello world"))
}
}
func main() {
ListenAndServe(":8080", &SimpleWeb{})
}
映射规则的实现是通过if...else...来实现的,当这里需要映射的规则越来越多之后,是不是会导致整个ServeHttp方法越来越庞大,比如下面展示这样子。其次如果有更加复杂的路由规则,这里也不好扩展。
func (s *SimpleWeb) ServeHttp(c *Conn, req *Request) {
if req.uri == "/hello" {
....
} else if{
....
} else if {
....
}
}
其次业务处理逻辑全部堆积在ServeHttp方法中,也会导致该方法非常臃肿
if req.uri == "/hello" {
//这里应该没有设置content-length头部字段,所以浏览器需要连接断开后,才知道数据已经读取完成了
c.WriteData([]byte("hello"))
} else {
c.WriteData([]byte("hello world"))
}
同时这里也将两部分的职责堆积在一起,也是不合适的,需要将其分割开来。
2.改造
为了将业务处理逻辑从SimpleWeb中的ServeHttp中抽取出来,可以定义一个接口,每一个请求处理器都实现该接口,可以复用之前的Handler接口。
type Handler interface {
ServeHttp(conn *Conn, request *Request)
}
然后SimpleWeb必须保存有请求和处理器的映射关系,此时需要在simpleWeb中保存这部分数据。
type SimpleWeb struct {
routes map[string]Handler
}
SimpleWeb保存的routes也需要创建该HTTP服务器的用户来指定其映射关系,此处就需要定义个方法,完成路径和处理器之间映射关系的注册,详细逻辑如下:
func (router *SimpleWeb) SetRoute(pattern string, handler Handler) {
//不支持 "" 或者 "/" 全匹配
if pattern == "" || pattern == "/" {
panic("http invalid pattern")
}
router.routes[pattern] = handler
}
SimpleWeb中的ServeHttp则需要实现具体的路由匹配规则,具体如下所示。这里只是简单实现了一个完全匹配规则。
func (router *SimpleWeb) ServeHttp(conn *Conn, request *Request) {
var h Handler
// 获取到url 和 其对应的处理器
for k, v := range router.routes {
if k == request.uri {
h = v
break
}
}
// 如果没有找到,直接返回
if h == nil {
return
}
// 执行具体的业务逻辑
h.ServeHttp(conn, request)
}
其实也可以在这基础上实现最长路由前缀的功能,比如存在/user/hello 和 /user 两个路由规则,此时存在一个请求url为 /user/hello/world, 并没有其完全对应的路由规则,此时便可以选择/user/hello 这个路径对应的handler来处理请求。所以这里的路由规则其实是可以变更的,并不是说一定要完全匹配的。
最后,SimpleWeb这部分逻辑可以作为通用逻辑,如果一个HTTP服务器并不需要太复杂的路由匹配规则,现在SimpleWeb实现的路由规则满足其需求。此时可以直接使用SimpleWeb来作为Http服务器的请求处理器,如同下面使用示例所示。
以上,已经解决了原本SimpleWeb中ServeHttp中路由映射规则以及业务逻辑耦合在一起的问题,也方便了路由映射规则的扩展。
3. 使用示例
- 当对SimpleWeb进行改造后,需要实现具体的业务处理器,实现ServeHttp接口
type HelloHandler struct {
}
func (handler *HelloHandler) ServeHttp(c *Conn, req *Request) {
c.WriteData([]byte("hello"))
}
- 启动Http服务器时,对业务处理器进行注册,然后调用ListenAndServe函数即可启动该服务器
func main() {
// 创建一个处理器
router := SimpleWeb{routes: map[string]Handler{}}
// 完成请求和处理器的映射关系
router.SetRoute("/hello", &HelloHandler{})
// 启动http服务器
ListenAndServe(":8080", &router)
}