前言
go的标准库http
已经封装好很多接口,可以很简单实现一个web服务器。1
2
3
4
5
6
7
8
9
10
11
12
13// 定义 handler
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
func main() {
//绑定pattern和handler
http.HandleFunc("/hello", HelloServer)
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
实现
从上面例子可以看到,一个url pattern对应一个handler,即对应一个处理,就可以处理http请求了,所以下面的实现是基于对这两个东西的封装开始
封装一个restful app 结构
1 | type App struct { |
初始化函数1
2
3
4
5
6
7
8
9
10
11
12
13func NewApp() *App {
return &App{
handlers: make(map[string]func(r *HttpRequest,w HttpResponse)error),
patterns:make([]string,0),
methods:make(map[string]string),
regexps:make(map[string]*regexp.Regexp),
pathparamanmes:make(map[string][]string),
//一个默认的异常处理,直接返回异常内容
errHandler: func(err error, r *HttpRequest, w HttpResponse) {
w.Write( []byte(err.Error()))
},
}
}
映射绑定
1 | func(a *App) handle(method string,pattern string, handler func(r *HttpRequest,w HttpResponse) error){ |
有了Restful接口的四个方法映射绑定,剩下的就要请求能进到来,所以接下来要写个入口才行。
编写http入口
1 | //http 入口 |
入口函数主要调用http
库来启动http服务,然后把请求处理函数作为ListenAndServe
第二个参数传入。这里由holder
来实现这个处理函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50func (h *hodler) ServeHTTP(w http.ResponseWriter, r *http.Request){
//封装一下,附加更多功能
request:= newHttpRequest(r)
response:=newHttpResponse(w)
//捕获panic,并让errhandler处理返回。
defer func() {
if err:=recover();err!=nil{
if e,ok:=err.(error);ok{
h.app.errHandler(InternalError{e,""},request,response)
}
if e,ok:=err.(string);ok{
h.app.errHandler(InternalError{nil,e},request,response)
}
}
}()
//根据pattern的添加顺序,循环判断
for _,p:=range h.app.patterns{
if reg,ok:= h.app.regexps[p];ok{
//匹配method
if method,ok:=h.app.methods[p];ok&&r.Method==method{
//匹配pattern
if reg.Match([]byte(r.URL.Path)) {
//抽取url path parameters
matchers:=reg.FindSubmatch([]byte(r.URL.Path))
pathParamMap:=make(map[string]string)
if len(matchers)>1{
if pathParamNames,ok:=h.app.pathparamanmes[p];ok{
for i:=1;i<len(matchers);i++{
pathParamMap[pathParamNames[i]]=string(matchers[i])
}
}
}
//PathParams是封装后的request独有的属性
request.PathParams=pathParamMap
if handler,ok:=h.app.handlers[p];ok{
//执行handler
err:=handler(request,response)
if err!=nil{
//执行errhandler
h.app.errHandler(err,request,response)
}
return
}
}
}
}
}
//执行no found errhandler
h.app.errHandler(NoFoundError{},request,response)
}
基本一个请求的流程如下:
requset->ServeHTTP()->匹配url pattern->匹配method->匹配到你的handler->执行你的handler->你的handler返回结果
返回结果
由于返回结果可以有很多,所以封装了http
库的http.ResponseWriter
来实现WriteString,WriteJson,WriteXml,WriteFile
等方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29//封装request,附件一个PathParams来保存url path parameters.
type HttpRequest struct {
*http.Request
PathParams map[string] string
}
type HttpResponse struct {
http.ResponseWriter
}
//用来返回字符
func (response *HttpResponse) WriteString(str string) error {
...
}
//返回JSON
func (response *HttpResponse) WriteJson(jsonObj interface{}) error {
...
}
//返回XML
func (response *HttpResponse) WriteXml(xmlObj interface{}) error {
...
}
//返回文件
func (response *HttpResponse) WriteFile(filepath string) error {
...
}
//返回一个模板html
func (response *HttpResponse) WriteTemplates(data interface{},tplPath ...string) error {
...
}
例子
1 | func main(){ |
收工😄
总结
go的标准库封装了很多了,所以实现这个其实还是比较轻松的😄
详细代码见https://github.com/ejunjsh/gorest