三. HTTP服务器的处理器映射器

100 阅读3分钟

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. 使用示例

  1. 当对SimpleWeb进行改造后,需要实现具体的业务处理器,实现ServeHttp接口
type HelloHandler struct {
}

func (handler *HelloHandler) ServeHttp(c *Conn, req *Request) {
   c.WriteData([]byte("hello"))
}
  1. 启动Http服务器时,对业务处理器进行注册,然后调用ListenAndServe函数即可启动该服务器
func main() {
   // 创建一个处理器
   router := SimpleWeb{routes: map[string]Handler{}}
   // 完成请求和处理器的映射关系
   router.SetRoute("/hello", &HelloHandler{})
   // 启动http服务器
   ListenAndServe(":8080", &router)
}