首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言经典库使用分析(三)| Gorilla Handlers 详细介绍

Go语言经典库使用分析(三)| Gorilla Handlers 详细介绍

作者头像
飞雪无情
发布2018-08-28 11:21:15
6010
发布2018-08-28 11:21:15
举报

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号flysnow_org或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

在我们编写web服务端程序的时候,我们可能会对一些甚至全部的Http Request统一处理,比如我们记录每个访问的Request,对提交的Form表单进行映射等,要达到这些目的,比较优雅的做法是Http 中间件。

中间件,顾名思义,强调的是中间,他是一种业务无关的,在正常的的业务handler处理前后的,独立的逻辑处理片段。一般调用顺序如下:

ServeMux路由分发->调用中间件1->调用中间件2……->调用真正的业务处理逻辑

因为中间件非常独立,可以我们不用的时候,去掉即可;需要用的时候,加上,并不会修改真正的业务处理逻辑代码,可谓非常简洁方便。

这里我选用Gorilla Handlers这个中间件库演示如何使用和定义一个中间件,这一篇主要讲Gorilla Handlers的使用,下一篇会讲Gorilla Handlers里每个中间件的实现原理。

安装

Gorilla Handlers是一个很简单,但是很有代表性的中间件库,所以拿他来分析,更容易理解handler中间件。在使用之前,我们要先安装,该中间件库已经托管在Github上,所以我们直接使用go get即可。

$ go get github.com/gorilla/handlers

安装之后,我们在代码里使用如下代码即可导入使用。

import "github.com/gorilla/handlers"

Gorilla Handlers以函数的方式提供了好几种中间件,比如CombinedLoggingHandler、CompressHandler、ContentTypeHandler、LoggingHandler等,下面我们就一一介绍他们。

LoggingHandler

我们应该都用过Nginx,Nginx的访问日志,类似于如下这样:

[05/Aug/2017:21:06:24 +0800] "GET /favicon.ico HTTP/1.1" 200 11

从中我们可以看到访问的什么资源,使用的什么协议,返回的HTTP状态以及请求的大小是多少,这种一种日志风格,和Apache Common Log Format很像,LoggingHandler中间件就是帮我们做这个事情的。

它可以记录request的日志,输出到一个io.Writer里。

func main() {
	http.Handle("/",useLoggingHandler(handler()))
	http.ListenAndServe(":1234",nil)
}

func handler() http.Handler{
	return http.HandlerFunc(myHandler)
}

func myHandler(rw http.ResponseWriter, r *http.Request) {
	rw.WriteHeader(http.StatusOK)
	io.WriteString(rw,"Hello World")
}


func useLoggingHandler(next http.Handler) http.Handler {
	return handlers.LoggingHandler(os.Stdout,next)
}

我这里的io.Writer是一个os.Stdout,也就是标准的控制台输出,所以我们访问http://localhost:1234/的时候,可以看到控制台打印的Request日志信息输出。

::1 - - [05/Aug/2017:21:06:24 +0800] "GET / HTTP/1.1" 200 11
::1 - - [05/Aug/2017:21:06:24 +0800] "GET /favicon.ico HTTP/1.1" 200 11

handlers.LoggingHandler函数的参数我们可以看出,它接受一个Handler,然后返回一个Handler,其实就是对现有的Handler的一次包装,这就是中间件。

这里的输出参数类型是io.Writer,所以我们可以输出文件等实现了该接口的任何类型。

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号flysnow_org或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

CombinedLoggingHandler

还有一种日志格式,这种日志格式输出的日志信息更详细,更全面,比如包含UA等信息,这种格式被称为Apache Combined Log Format。

CombinedLoggingHandler就是为我们提供输出一种这种格式的中间件,使用方式和LoggingHandler一样。

func useCombinedLoggingHandler(next http.Handler) http.Handler {
	return handlers.CombinedLoggingHandler(os.Stdout,next)
}

看下这个中间件输出的日志信息

::1 - - [05/Aug/2017:21:39:45 +0800] "GET /favicon.ico HTTP/1.1" 200 11 "http://localhost:1234/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"

UA+HOST,日志信息更全面了。

CompressHandler

这是一个压缩response的中间件,支持gzip和deflate压缩。如果客户端的请求头里包含Accept-Encoding,并且值为gzip或者deflate,该中间件就会压缩返回的response,这样就可以减少response的大小,减少响应的时间,使用访问也很简单,这里简单举个例子。

func main() {
	http.Handle("/gzip",useCompressHandler(handler()))
	http.ListenAndServe(":1234",nil)
}

func handler() http.Handler{
	return http.HandlerFunc(myHandler)
}

func myHandler(rw http.ResponseWriter, r *http.Request) {
	rw.WriteHeader(http.StatusOK)
	rw.Header().Set("Content-Type", "text/plain")
	io.WriteString(rw,"Hello World")
}

func useCompressHandler(next http.Handler) http.Handler {
	return handlers.CompressHandler(next)
}

这里尤其注意的是我们返回的response的内容类型要特别指定一下,不然就会被自动解析为gzip,就变成下载一个gz文件了。我这里强制指定文本类型,这样就可以看到返回的内容Hello World

该中间件还有一个函数CompressHandlerLevel可以指定压缩的级别,级别是gzip.BestSpeed和gzip.BestCompression之间的值,如果大家不想用默认压缩级别,可以使用这个函数指定。

还记得Nginx可以开启Gzip加速吧,差不多也是这么个实现。

ContentTypeHandler

这也是一个很有意思的中间件,他的作用是只处理支持的内容类型,如果不支持,则返回415状态码。该中间件只对PUT,POST,PATCH方法有效,其他方法则不做处理,也就相当于没有使用这个中间件。

func useContentTypeHandler(next http.Handler) http.Handler {
	return handlers.ContentTypeHandler(next,"application/x-www-form-urlencoded")
}

最后一个参数是可变参数,我们可以指定多个ContextType类型,这些类型是被我们支持的,其他类型则不支持。如果我们使用PUT、POST、PATCH方法提交的请求的内容类型不在我们支持的内容类型范围内,则返回415错误。

CanonicalHost

这是一个重定向的中间件,他可以把一个Request请求重新定向到另外一个域名上,并且会带上原请求的Path和Query。

func main() {
	http.Handle("/flysnow",useCanonicalHost(handler()))
	http.ListenAndServe(":1234",nil)
}

func handler() http.Handler{
	return http.HandlerFunc(myHandler)
}

func myHandler(rw http.ResponseWriter, r *http.Request) {
	rw.WriteHeader(http.StatusOK)
	rw.Header().Set("Content-Type", "text/plain")
	io.WriteString(rw,"Hello World")
}

func useCanonicalHost(next http.Handler) http.Handler {
	return handlers.CanonicalHost("http://www.flysnow.org/",http.StatusFound)(next)
}

上面的示例,当我们在浏览器内输入http://localhost:1234/flysnow的访问的时候,会自动跳转到http://www.flysnow.org/flysnow

小结

其他的一些不常用的中间的使用方式也类似,大家可以自己看下文档测试下。Go Http的中间件,有点类似于HTTP的拦截器,通过一层层的包装,我们可以组成一个中间件的处理链,便于我们处理我们需要处理的通用性问题,如果哪个中间件不想要,去掉即可,不用对业务代码做任何修改。

例子中的演示,都是一个url对应一个中间件的处理,这个主要是为了演示方面。如果我们相对所有的请求都使用某个中间件怎么做呢?肯定不能使用我们例子中的方式了,因为这样会写很多个。

对于以上这种方式,我们可以借助HTTP Mux路由,因为一个路由也是一个Handler,只用对这个路由应用中间件,就可以拦截处理所有的请求了。

下一篇开始分享这些常用中间件的实现原理和源代码的分析。

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号flysnow_org或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年8月6日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装
  • LoggingHandler
  • CombinedLoggingHandler
  • CompressHandler
  • ContentTypeHandler
  • CanonicalHost
  • 小结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档