Golang 学习手册
  • 主页
  • 安装
  • 参考资料
  • Q&A
  • 基础
    • Hello,world!
    • 包
    • 变量
    • 常量
    • 类型
      • 整型
      • 浮点型
      • 复数
      • 布尔型
      • 字符
      • 字符串
      • 数组
      • 切片
      • 字典
      • 指针
      • 方法
      • 结构体
      • 接口
      • 自定义类型
    • 语法
      • 判断
      • 循环
      • 错误处理
    • 协程
      • 锁
      • 等待组
      • 通道
      • 上下文
    • 测试与分析
      • 单元测试
      • 性能测试
      • 性能分析
    • 编译
      • 条件编译
      • 交叉编译
  • 高级
    • 存储
      • 键值存储
        • Etcd
    • HTTP
      • http服务基础
      • Websocket
      • 开源框架
        • Beego
        • Echo
        • Gin
        • Iris
        • Revel
        • 框架对比
      • Kubernetes风格API框架
    • RPC
      • RPCX
      • GRPC
      • grpc-gateway
    • 连接分发器
    • TLS加密
    • 链路追踪
      • skywalking
  • 项目管理
    • 代码规范
    • 包管理
    • 文档
      • GoDoc
      • Markdown
      • Swagger
    • 仓库管理
      • 分支管理
      • 问题管理
      • 里程碑管理
      • 发布管理
    • 持续集成
      • CircleCI
      • TravisCI
由 GitBook 提供支持
在本页
  • Handler
  • ServeMux
  • Server
  • Middleware

这有帮助吗?

在Git上编辑
  1. 高级
  2. HTTP

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!!!

上一页HTTP下一页Websocket

最后更新于5年前

这有帮助吗?