[TOC] 当用户访问某个URI时,Web Server能够精确的调用特定的服务接口提供服务,这些都是通过“服务注册”来实现的。Web Server提供服务需要回调函数/方法/对象/控制器的支持,ghttp包支持多种服务注册模式,为开发者提供非常强大和灵活的接口功能。服务注册是整个Web Server最核心的部分,也是gf框架中最精心设计的一个模块。本章节将会进行详细介绍。 服务注册管理由ghttp包提供,API文档地址:[godoc.org/github.com/johng-cn/gf](https://godoc.org/github.com/johng-cn/gf/g/net/ghttp)。 >[danger] # g与ghttp包 在随后的章节中,我们将会看到频繁的```g.Server()```及```ghttp.GetServer()```混用,其实它们获取的都是同一个Web Server单例对象指针。其中```ghttp.GetServer()```是ghttp包原生的单例Web Server对象指针获取方法。而```g.Server()```是框架通用对象管理器提供的方法,框架```g.*```对象管理器封装了常用的一些对象方法,具体请参看后续的【[对象管理](单例管理.md)】章节。虽然这种方式模块间耦合性比较高,但使用简便,也是推荐的使用方式。 >[danger] # 服务注册介绍 本章开始之前,我们再来看一下本手册开头的Hello World程序: ```go package main import ( "gitee.com/johng/gf/g" "gitee.com/johng/gf/g/net/ghttp" ) func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request) { r.Response.Write("哈喽世界!") }) s.Run() } ``` 其中,使用```BindHandler```方法进行服务注册的方式叫做“回调函数注册”,是最简单的一种服务注册方式。通过给指定的Web Server上对应的URI注册一个可执行的方法,当客户端访问该URI时,Web Server便自动调用对应注册的回调函数来执行处理。在回调函数注册中,每个注册函数都会有一个```ghttp.Request```对象参数指针,表示每个请求特定的独立的请求处理对象,回调函数可以通过该对象获取提交请求参数,也可以返回处理结果数据。 >[success] ## 服务注册方式比较 在详细讲解每一种注册方式之前,先看看每种注册方式各自的优缺点,以便在不同的业务场景中选择更适合的注册方式。如果暂时不理解这个表格没有关系,可以在了解完每一种注册方式之后再回过头来看,也许会更清晰。 | 注册方式 | 使用难度 | 安全系数 | 执行性能 | 内存消耗 | | --- | --- | --- | --- | ---| | 控制器注册 | 低 | 高 | 低 | 高 | | 执行对象注册 | 中 | 中 | 中 | 中 | | 回调函数注册 | 高 | 低 | 高 | 低 | 比较指标说明: 1. 使用难度:主要指对于执行流程以及数据管理维护的复杂度; 1. 安全系数:主要指在异步多协程下的数据安全管理维护; 1. 执行性能:执行性能,相对比较的结果; 1. 内存消耗:内存消耗,相对比较的结果; >[success] ## 服务注册方法列表 ```go func (s *Server) BindController(pattern string, c Controller) error func (s *Server) BindControllerMethod(pattern string, c Controller, methods string) error func (s *Server) BindControllerRest(pattern string, c Controller) error func (s *Server) BindObject(pattern string, obj interface{}) error func (s *Server) BindObjectMethod(pattern string, obj interface{}, methods string) error func (s *Server) BindObjectRest(pattern string, obj interface{}) error func (s *Server) BindHandler(pattern string, handler HandlerFunc) error ``` 其中```BindController*```方法用于控制器相关注册,```BindObject*```方法用于对象相关注册,```BindHandler```方法用于特定的回调函数注册。 服务注册使用的pattern参数格式如下: [HttpMethod:]路由规则[@域名] 其中HttpMethod(支持的Method:```GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE```)和域名为非必需参数,一般来说直接给定路由规则(路由规则分为**静态路由**和**动态路由**,为便于演示本章节所有服务注册均采用静态路由规则,路由规则的详细介绍请查看【[路由控制](路由控制.md)】章节)参数即可。因为需要使用HttpMethod注册的情况大多数为RESTful控制器,直接使用RESTful相关方法注册即可,域名支持也可以使用Domain方法来进行绑定。 此外```BindController*```系列方法第二个参数为控制器接口,给定的参数必须实现```ghttp.Controller```接口。简便的做法是用户自定义的控制器直接继承```gmvc.Controller```基类即可,```gmvc.Controller```已经实现了对应的接口方法。 >[success] ## 域名服务注册方法 服务注册支持绑定域名,以下是对应的方法列表: ```go func (d *Domain) BindController(pattern string, c Controller) error func (d *Domain) BindControllerMethod(pattern string, c Controller, methods string) error func (d *Domain) BindControllerRest(pattern string, c Controller) error func (d *Domain) BindObject(pattern string, obj interface{}) error func (d *Domain) BindObjectMethod(pattern string, obj interface{}, methods string) error func (d *Domain) BindObjectRest(pattern string, obj interface{}) error func (d *Domain) BindHandler(pattern string, handler HandlerFunc) error ``` 各项参数说明和Server的对应方法一致,只不过在Domain对象的底层会自动将方法绑定到Domain指定的域名列表中,只有对应的域名才能提供访问。 我们来看一个简单的例子,我们将前面的Hello World程序改成如下形式: ```go package main import "gitee.com/johng/gf/g/net/ghttp" func main() { ghttp.GetServer().Domain("localhost").BindHandler("/", func(r *ghttp.Request) { r.Response.Write("Hello World!") }) } ``` 我们再次使用```http://127.0.0.1/```进行访问,发现Web Server返回404,为什么呢?因为该程序中的回调函数只注册到了localhost域名中,其他域名自然无法访问。当然,前面也提到Domain方法的**域名参数支持多个**,自定义域名的服务注册相当方便。 >[success] ## 服务注册初始化 **所有的服务注册统一在包的init初始化方法中完成**(init是Go语言内置的包初始化方法,并且一个包中支持多个init方法),一个包可以包含多个文件,每个文件都可以有一个init初始化方法,可以分开注册,在使用的时候会通过同一个包引入进程序,自动调用初始化方法完成注册。可以参考示例文件。 来看一个例子: gitee.com/johng/gf/blob/master/geg/frame/mvc/main.go ```go package main import ( "gitee.com/johng/gf/g/net/ghttp" _ "gitee.com/johng/gf/geg/frame/mvc/controller/demo" ) func main() { ghttp.GetServer().SetPort(8199) ghttp.GetServer().Run() } ``` 其中通过: ```go import _ "gitee.com/johng/gf/geg/frame/mvc/controller/demo" ``` 这样一条类似于all in one的语句便完成了对包中的所有控制器的引入和注册(当然,包中的init应当实现注册方法调用),在demo包中包含了多个控制器、执行对象、回调函数的注册,demo包具体的控制器注册以及相关逻辑我们将在后续章节继续介绍。