Gogee_Day1 HTTP基礎・APIの構築

Last renew: April 7, 2022 pm

Goでウェブサービスを動かす

注:本文仅为个人学习笔记,无任何版权。

Go语言内置了net/http库,并且封装了http网络编程的基础接口。这样我们可以很便利的实现web接入。Gogee也是基于net/http的框架。举个例子:

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
package main

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

func main() {
// set 2 route- "/" and "/hello"
http.HandleFunc("/", indexHandler)
http.HandleFunc("/hello", helloHandler)
// launch web service
// :9999 : address AT 9999 port
// nil : solve instance by Standard library
log.Fatal(http.ListenAndServe(":9999", nil))
}

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

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

![截屏2022-03-08 11.44.50](Gogee-Day1-HTTP基礎・APIの構築/截屏2022-03-08 11.44.50.png)

http.handler API の実現

handler是什么呢?查看一下net/http的源码。

1
2
3
4
5
6
7
package http

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

func ListenAndServe(address string, h Handler)error
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

#### ServeHTTPとは

在Golang中,你要构建一个web服务,必然要用到```http.ListenAndServe```

对于```ListenAndServe```来说,他的方法签名为```http.ListenAndServe(address, handler)```

所以,如果想要用```ListenAndServe``` 两种方式。

1. 自己定一个struct,并在上面实现```ServeHTTP```方法。

```go
type myhandler int

func(m myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
-, err := fmt.Fprintln(w, "hello this is golang ServeHTTP")
if err != nil{
fmt.Println("the error occured:", err)
}else{
fmt.Println("someone is accessing:" r.Method)
}
}

func main(){
var hello myHandler
fmt.println("begin to listen")
http.ListenAndServe(":9999", hello)
}
  1. 使用nil作为handler。

    nil在golang的http包下的server.go文件中有过定义。

    1
    2
    3
    if handler == nil{
    handler = DefaultServeMux
    }

    可以看到在ListenAndServe中传入nil,本质上就是在调用DefaultServeMux

所以,利用ListenAndServe的第一个特点,我们可以构建自己的structengine,并在上面实现Serve HTTP

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
package main

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

// Engine is the uni handler for all requests
type Engine struct{}

// ResponseWriter: create the response of this HTTP request.
// http.Request: all the information of this HTTP request. For example, the address, Header and Body

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request){
switch req.URL.Path{
case "/":
fmt.Fprintf(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() {

// Transfer all the request to our own solution.
engine := new(Engine)
// launch web service
// :9999 : address AT 9999 port
//
log.Fatal(http.ListenAndServe(":9999", engine))
}

在实现了engine之后,我们拦截了所有的HTTP请求,拥有了统一的控制入口,并且可以自由定义这些请求的路由映射的规则,或者统一处理他们。到这里我们就可以发现我们走出了实现web框架的第一步。

Gogeeフレームワークの骨組み

我们所设想的框架目录结构是这样的。

1
2
3
4
5
Gogee/
|--Gogee.go
|--go.mod
main.go
go.mod

把Gogee与main分开放两个文件夹的原因是 Go语言的package对于每个文件夹有且只能有一个,所以Gogee的package和main的package重复了

go.mod

借用go module来管理依赖

1
2
3
4
5
6
7
8
module Gogee

go 1.17

require Gogee v0.0.0

// Reference relative path
replace Gogee => ./Gogee

replace将Gogee指向 ./Gogee

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"net/http"

"Gogee"
)

func main() {
r := Gogee.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")
}

使用New()创建Gogee的实例,使用GET()添加路由,最后使用Run()启动Web服务。(目前只是静态路由)

Gogee.go

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
50
51
package Gogee

import (
"fmt"
"net/http"
)

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

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

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

// addRoute is the way to add something to route
func (engine *Engine) addRoute(method string, 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 POST request
func (engine *Engine) POST(pattern string, handler HandlerFunc){
engine.addRoute("POST", pattern, handler)
}

//Run definesd the method to start a http server
func (engine *Engine) Run(addr string) (err error){
return http.ListenAndServe(addr, engine)
}

// To use the ListenAndServe, we need to set a ServeHTTP struct
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
key := req.Method + "-" + req.URL.Path
if handler, ok := engine.router[key]; ok {
handler(w, req)
} else {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "404 MOT FOUND: %s\n", req.URL)
}
}

首先定义了type HandlerFunc,这是提供给框架用户的,用来定义路由映射的处理方法。

我们在Engine中,添加了一张路由映射表router, key由请求方法与静态路由地址构成,比如GET-/GET-/helloPOST-/hello,这样针对相同的路由,如果请求的方式不同,可以映射不同的Handler

当用户调用(*Engine).GET()方法时,会将路由和处理方法注册到router中(addroute方法)。(*Engine).Run()方法,是ListenAndServe的封装。

Engine实现的ServeHTTP方法的作用是解析请求的路径,查找router,如果查到,执行注册的处理方法,如果查不到,返回404 NOT FOUND。

执行go run main.go再用curl访问即可。