http服务基础

Handler

内置包net/http提供了一系列与http相关的方法,包括服务端与客户端。通过http.ListenAndServe()方法侦听端口并启动http服务,当服务通过端口接收到请求后,需要根据http的请求路径将该请求分配给指定的处理方法,http.HandleFunc()实现了请求路径与handler方法的绑定,由handler方法完成请求的处理和响应。

例子:使用http.HandleFunc()处理http请求

package main

import (
	"fmt"
	"net/http"
)

// HelloServer 一个http.HandlerFunc,响应hello,world!
func HelloServer(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("hello,world!\n"))
}

func main() {
	// 将所有匹配到路径'/'的请求交由HelloServer方法处理
	http.HandleFunc("/", HelloServer)
	// 开启http服务并侦听到8080端口上,在不填地址的情况下会侦听所有地址
	if err := http.ListenAndServe("localhost:8080", nil); err != nil {
		fmt.Println(err)
	}
}

执行命令go run main.go启动http服务,此时控制台会阻塞挂起

开启一个新的命令行控制台,执行curl 127.0.0.1:8080访问http服务,输出结果为

hello,world!

除了http.HandleFunc()外,也可以用http.Handle()完成相同的事情,区别是http.HandleFunc()中第二个参数是一个方法,而http.Handle()中是一个http.Handler接口,http.Handler接口定义如下

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

在http.Handle()方法中会将请求交由http.Handler接口中的ServeHTTP()方法处理并响应,需要在自定义的类型中实现ServeHTTP()方法

例子:使用http.Handle()处理http请求

package main

import (
	"fmt"
	"net/http"
)

// HelloServer 实现了http.Handler接口,可以用于处理http请求
type HelloServer struct {
}

// ServeHTTP 用于处理http请求并响应结果,返回hello,world!
func (s HelloServer)ServeHTTP(res http.ResponseWriter, req *http.Request) {
	res.Write([]byte("hello,world!\n"))
}

func main() {
	// 将对于路径/下的请求交由实现了http.Handler接口的一个HelloServer对象处理
	http.Handle("/", HelloServer{})
	if err := http.ListenAndServe("localhost:8080", nil); err != nil {
		fmt.Println(err)
	}
}

执行命令go run main.go启动http服务

开启一个新的命令行控制台,执行curl 127.0.0.1:8080访问http服务,输出结果为

hello,world!

在方法http.ListenAndServe()中,第一个参数为监听地址;第二个参数表示服务端处理程序, 通常为空,这意味着服务端调用http.DefaultServeMux进行处理。http.DefaultServeMux是默认的路由分发器,而服务端编写的业务逻辑处理程序http.Handle()或http.HandleFunc() 默认将路由注入 http.DefaultServeMux 中。

ServeMux

除了使用默认的路由分发器之外,还可以使用自定义的路由分发器。

首先,通过http.NewServeMux()方法就可以创建一个自定义的路由分发器,在该分发器里面也封装了Handle()和HandleFunc()方法,在调用http.ListenAndServe()方法时,将路由分发器做为第二个参数传入即可生效。

例子:自定义ServerMux

package main

import (
	"fmt"
	"net/http"
)

// 以func(http.responseWriter,*http.Request)格式实现了一个http请求的handler方法
func HelloServer(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("hello,world!\n"))
}

func main() {
	// 创建一个自定义路由分发器
	mux := http.NewServeMux()
	// 将所有匹配到路径'/'的请求交由HelloServer方法处理并注入到自定义路由分发器中
	mux.HandleFunc("/", HelloServer)
	// 开启http服务并侦听到8080端口上,所有的请求都交由自定义路由分发器处理
	if err := http.ListenAndServe("localhost:8080", mux); err != nil {
		fmt.Println(err)
	}
}

执行命令go run main.go启动http服务

开启一个新的命令行控制台,执行curl 127.0.0.1:8080访问http服务,输出结果为

hello,world!

Server

http.ListenAndServe会使用默认配置开启一个Server,与ServeMux一样,Server也是可以自定义的。通过实例化一个http.Server对象即可创建

例子:自定义Server

package main

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

// Wait 等待6秒后返回hello,world!
func Wait(w http.ResponseWriter, req *http.Request) {
	time.Sleep(6 * time.Second)
	w.Write([]byte("hello,world!\n"))
}

// Hello 返回hello,world!
func Hello(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("hello,world!\n"))
}

func main() {
	// 创建一个自定义路由分发器
	mux := http.NewServeMux()

	// 为自定义路由分发器绑定路由方法
	mux.HandleFunc("/hello", Hello)
	mux.HandleFunc("/wait", Wait)

	// 实例化一个自定义Server
	server := http.Server{
		Addr:         "localhost:8080",
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler:      mux,
	}

	// 启动自定义Server
	if err := server.ListenAndServe(); err != nil {
		fmt.Println(err)
	}
}

执行命令go run main.go启动http服务

开启一个新的命令行控制台

执行curl 127.0.0.1:8080/hello输出结果

hello,world!

执行curl 127.0.0.1:8080/wait输出结果

curl: (52) Empty reply from server

Middleware

对于http服务,不同的请求路径会对应到不同的处理方法上,如果我们需要对这些方法做日志采集或异常处理等通用的行为,在每个处理方法体中去调用这些功能的实现是比较麻烦的,而且也不够优雅。

我们可以使用装饰器模式实现一个中间层,在ServeMux路由分发器和HandlerFunc处理方法之间生效,让我们在不需要修改处理方法的情况下,为处理方法附加一些通用的功能。

例子:http处理方法中间件

package main

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

// LoggerMiddleware 日志中间件,为处理方法封装一层日志输出
func LoggerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		log.Printf("Request %s %s", r.Method, r.URL.Path)
		next.ServeHTTP(w, r)
		log.Printf("Response %s in %v", r.URL.Path, time.Since(start))
	})
}

// PanicMiddleware 异常处理中间件,为处理方法封装异常处理逻辑
func PanicMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			e := recover()
			if err, ok := e.(error); ok {
				log.Println(err)
				w.Write([]byte(err.Error()))
			} else {
				log.Println(e)
				w.Write([]byte("unknown error"))
			}
		}()
		next.ServeHTTP(w, r)
	})
}

// Hello 一个http.HandlerFunc处理方法,返回hello,world!
func Hello(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("hello,world!\n"))
}

// Panic 一个http.HandlerFunc处理方法,引发一个异常
func Panic(w http.ResponseWriter, req *http.Request) {
	panic("oh,we have got a panic!!!")
}

func main() {
	// 为/hello路径下的处理方法Hello套上日志输出中间件
	http.Handle("/hello", LoggerMiddleware(http.HandlerFunc(Hello)))
	// 为/panic路径下的处理方法Panic套上日志输出和异常处理中间件
	http.Handle("/panic", PanicMiddleware(LoggerMiddleware(http.HandlerFunc(Panic))))
	http.ListenAndServe("localhost:8080", nil)
}

服务端执行命令go run main.go启动http服务

开启一个新的命令行控制台

客户端执行curl 127.0.0.1:8080/hello

客户端输出

hello,world!

服务端输出

2019/06/25 10:46:47 Request GET /hello
2019/06/25 10:46:47 Response /hello in 16.0028ms

客户端执行curl 127.0.0.1:8080/panic

客户端输出

unknown error

服务端输出

2019/06/25 10:47:32 Request GET /panic
2019/06/25 10:47:32 oh,we have got a panic!!!

最后更新于