Go Web学习(2)——实现中间件(middleware)

昨天咱们探讨了Go语言使用标准库实现简单的web版的HelloWorld,大体了解了Go实现server应用的流程,今天咱们来探讨一下用Go语言实现http的Middleware。web

咱们知道,绝大部分web应用会将逻辑与功能的实现写在middleware里面更整个结构更加分明,middleware大体在web应用里面大体分为两种,即处理response和处理request(我的拙见,若有错误请以指正),接下来我将以处理request请求的形式来实现两种middleware的写法。编程

第一种 以类型的形式实现

上篇博客中,咱们探讨过Go语言实现Web最核心的部分:bash

http.ListenAndServe(":8000", handler)
复制代码

http包里面的ListenAndServe函数接受两个参数,即监听地址和处理接口handler,handler是一个接口,咱们须要实现这个接口中的惟一方法ServeHTTP即可以实现上述的函数,所以咱们处理的整个逻辑和流程都会在这个handler里面,下面咱们先来看一个最简单的handler实现。服务器

package main

import (
	"net/http"
)

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
}

func main() {
	http.ListenAndServe(":8000", http.HandlerFunc(myHandler))
}
复制代码

上面的代码中咱们定义一个myHandler,它接受http.ResponseWriter,*http.Request两个参数而后再向ResponseWriter中写入Hello World,在main函数中,咱们直接使用了ListenAndServe方法监听本地的8000端口,注意因为Go语言的强类型性,ListenAndServe的第二个参数类型是Handler,所以咱们想要将myHandler传递给ListenAndServe就必须实现ServeHTTP这个方法。但其实Go源码里面已经帮咱们实现了这个方法。curl

// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
复制代码

能够看到,Go语言将func(ResponseWriter, *Request)这种类型的函数直接定义了类型HandlerFunc,并且还实现了ServeHTTP这个方法,可是这个方法自己并无实现任何逻辑,须要咱们本身来实现。所以咱们实现了myHandler这个方法,它将输出一个最简单的HelloWorld响应。随后咱们能够用curl来测试一下:函数式编程

$ curl localhost:8000
Hello World
复制代码

能够看到,咱们经过curl请求本地的8000端口,返回咱们一个HelloWorld。这即是一个最简单的Handler实现了。可是咱们的目标是实现中间件,有了上述的所采用的的方法咱们就能够大体明白,myHandler应该做为最后的调用,在它以前才是中间件应该做用的地方,那么咱们有了一个大体的方向,咱们能够实现一个逻辑用来包含这个myHandler,但它自己也必须实现Handler这个接口,由于咱们要把它传递给ListenAndServe这个方法。好,咱们先大体阐述一下这个中间件的做用,它会拦截一切请求除了这个请求的host是咱们想要的host,固然这个host有咱们定义。函数

type SingleHost struct {
	handler     http.Handler
	allowedHost string
}
复制代码

因而咱们定义了一个SingleHost的结构体,它里面有两个成员一个是Handler,它将是咱们上述的myHandler,另外一个是咱们容许来请求Server的用户,这个用户他有惟一的Host,只有当他的Host知足咱们的要求是才让他请求成功,不然一概返回403。测试

由于咱们须要将这个SingleHost实例化并传递给ListenAndServe这个方法,所以它必须实现ServeHTTP这个方法,因此在ServeHTTP里面能够直接定义咱们用来实现中间件的逻辑。即除非来请求的用户的Host是allowedHost不然一概返回403。ui

func (this *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if (r.Host == this.allowedHost) {
		this.handler.ServeHTTP(w, r)
	} else {
		w.WriteHeader(403)
	}
}
复制代码

好,能够清楚的看到只有Request的Host==allowedHost的时候,咱们才调用handler的ServeHTTP方法,不然返回403.下面是完整代码:this

package main

import (
	"net/http"
)

type SingleHost struct {
	handler     http.Handler
	allowedHost string
}

func (this *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if (r.Host == this.allowedHost) {
		this.handler.ServeHTTP(w, r)
	} else {
		w.WriteHeader(403)
	}
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
}

func main() {
	single := &SingleHost{
		handler:http.HandlerFunc(myHandler),
		allowedHost:"refuse.com",
	}
	http.ListenAndServe(":8000", single)
}

复制代码

而后咱们用curl来请求本地的8000端口,

$ curl --head localhost:8000
HTTP/1.1 403 Forbidden
Date: Sun, 21 Jan 2018 08:32:47 GMT
Content-Type: text/plain; charset=utf-8
复制代码

能够看到咱们在中间件中实现了只容许host为refuse.com来访问的逻辑实现了,因为curl的Host是localhost因此咱们的服务器直接返回了它一个403。接下来咱们改变一下allowedHost

allowedHost:"localhost:8000",
复制代码

咱们将allowedHost变成为localhost:8000,而后用curl测试

$ curl localhost:8000
Hello World
复制代码

能够看到curl经过了中间件的并直接得到了myHandler返回的HelloWorld。

第二种 以函数的形式实现

好,在上面咱们实现了以类型为基础的中间件,可能对Node.js较熟悉的人都习惯以函数的形式实现中间件。首先,由于咱们是以函数来实现中间件的所以这个函数返回的即是Handler,它会接受两个参数,一个是咱们定义的myHandler,一个是allowedHost。

func SingleHost(handler http.Handler, allowedHost string) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == allowedHost {
			handler.ServeHTTP(w, r)
		} else {
			w.WriteHeader(403)
		}
	}
	return http.HandlerFunc(fn)
}
复制代码

能够看到,咱们在函数内部定义可一个匿名函数fn,这个匿名函数即是咱们要返回的Handler,若是请求用户的Host知足allowedHost,即可以将调用myHandler的函数返回,不然直接返回一个操做403的函数。整个代码以下:

package main

import "net/http"

func SingleHost(handler http.Handler, allowedHost string) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == allowedHost {
			handler.ServeHTTP(w, r)
		} else {
			w.WriteHeader(403)
		}
	}
	return http.HandlerFunc(fn)
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
}

func main() {
	single := SingleHost(http.HandlerFunc(myHandler), "refuse.com")
	http.ListenAndServe(":8000", single)
}
复制代码

咱们仍是经过curl来测试一下

$ curl --head localhost:8000
HTTP/1.1 403 Forbidden
Date: Sun, 21 Jan 2018 08:45:39 GMT
Content-Type: text/plain; charset=utf-8
复制代码

能够看到因为不知足refuse.com的条件,咱们会获得一个403,让咱们将refuse.com改成localhost:8000测试一下。

$ curl localhost:8000
Hello World
复制代码

与刚才同样咱们获得了HelloWorld这个正确结果。好,咱们经过以函数的形式实现了上面一样的功能,固然,这两种方法均可行,主要看我的喜爱了,喜欢函数式编程的我仍是喜欢后者(笑)。今天就到这里了,祝掘金越办越好!!!

相关文章
相关标签/搜索