在Golang的HTTP请求中共享数据

栏目: Go · 发布时间: 5年前

内容简介:首先,我们需要先明确一下问题的描述:本文所要讨论的共享数据可不是指的 cookie、session 之类的概念,它们描述的是在「请求间」共享数据,而我们关注的是在「请求中」共享数据,也就说是,在每个请求中的各个 middleware 和 handler 之间共享数据。实际上,我之所以关注这个问题源自而官方的 http.Handler 参数定义是:

首先,我们需要先明确一下问题的描述:本文所要讨论的共享数据可不是指的 cookie、session 之类的概念,它们描述的是在「请求间」共享数据,而我们关注的是在「请求中」共享数据,也就说是,在每个请求中的各个 middleware 和 handler 之间共享数据。

实际上,我之所以关注这个问题源自 httprouter ,众所周知,httprouter 是目前 Golang 社区最流行的 HTTP 路由库,不过它有一个问题,其 handler 参数定义如下:

func (http.ResponseWriter, *http.Request, httprouter.Params)

而官方的 http.Handler 参数定义是:

func (http.ResponseWriter, *http.Request)

也就是说, httprouter 多了一个 httprouter.Params 参数,用来传递路由参数,可惜它破坏了兼容性,关于此问题,官方给出了 说明

The router itself implements the http.Handler interface. Moreover the router provides convenient adapters for http.Handlers and http.HandlerFuncs which allows them to be used as a httprouter.Handle when registering a route. The only disadvantage is, that no parameter values can be retrieved when a http.Handler or http.HandlerFunc is used, since there is no efficient way to pass the values with the existing function parameters. Therefore httprouter.Handle has a third function parameter.

大概意思是 httprouter 提供了兼容模式,不过兼容模式不能使用路由参数。那么能不能在保持兼容性的前提下使用路由参数呢,官方有过 讨论 ,计划在新版本中使用 Context 来传递路由参数,但是几年过去了,还没实现。

让我们先顺着 Context 来看看如何在 Golang 的 HTTP 请求中共享数据。

路由的例子有点复杂,我们不妨假设一个简单点儿的例子:设想一下我们需要给每一个请求分配一个请求 ID,并且每个 middleware 或者 handler 都可以拿到此请求 ID。很明显,这个请求 ID 就是我们说的共享数据,下面让我们看看如何用 Context 来实现它:

package main

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

// RequestContextKey is a context key
type RequestContextKey string

func requestID(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), RequestContextKey("id"), "uuid")
		next(w, r.WithContext(ctx))
	}
}

func test1(w http.ResponseWriter, r *http.Request) {
	id := r.Context().Value(RequestContextKey("id"))
	w.Write([]byte("request_id: " + id.(string)))
}

func test2(w http.ResponseWriter, r *http.Request) {
	id := fmt.Sprintf("%v", r.Context().Value(RequestContextKey("id")))
	w.Write([]byte("request_id: " + id))
}

func main() {
	http.Handle("/test1", requestID(test1))
	http.HandleFunc("/test2", test2)
	http.ListenAndServe(":8080", nil)
}

本例只用到了两个 Context 方法,分别是:

  • WithValue(parent Context, key, val interface{}) Context
  • Value(key interface{}) interface{}

如上可见,key 和 val 都是 interface{},也就是说,你可以使用任意值作为键和值,与此对应的,当你取回值得时候,同样需要做对应的类型转换。

需要着重说明的一点是,最好不要使用基础类型来做 key,而应该使用自定义类型,就好像本例中的 RequestContextKey 类型,为什么要这样做?假设大家都是用 string 之类的基础类型来做 key 的话,那么我们就不容易区分这个 key 到底隶属于谁,很容易出现彼此影响的情况,Context 在读写数据的时候会保证类型安全,不会发生错乱的情况。

明白了这些就可以运行代码了,先请求 /test1,再请求 /test2,结果依次是:

  1. request_id: uuid
  2. request_id: <nil>

也就是说,我们实现了在 HTTP 请求中共享数据的功能,同时可知 Context 的作用范围是请求级的,不同请求的 Context 不会彼此干扰。

让我们把目光回到文章开头提到的 httprouter 身上,虽然它本身 和 http.Handler 有不兼容的问题,但是我们可以通过前面学到的 Context 相关知识来改善此问题:

package main

import (
	"context"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

// RouterContextKey is a context key
type RouterContextKey string

func compatible(next http.HandlerFunc) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		ctx := context.WithValue(r.Context(), RouterContextKey("params"), p)
		next(w, r.WithContext(ctx))
	}
}

func user(w http.ResponseWriter, r *http.Request) {
	p := r.Context().Value(RouterContextKey("params")).(httprouter.Params)
	w.Write([]byte(p.ByName("name")))
}

func main() {
	router := httprouter.New()
	router.GET("/user/:name", compatible(user))
	http.ListenAndServe(":8080", router)
}

本文是仓促写于返京的途中,未做严格验证,如有谬误敬请海涵。


以上所述就是小编给大家介绍的《在Golang的HTTP请求中共享数据》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

社交网站界面设计

社交网站界面设计

Christian Crumlish、Erin Malone / 樊旺斌、师蓉 / 机械工业出版社 / 2010-9-1 / 69.00元

《社交网站界面设计》提供100多种模式、原则以及最佳实践,并针对在设计社交网站时经常遇到的问题给出明确建议。本书将提供给你培养用户交互习惯和构建社区最具价值的参考。 本书作者将与你分享难得的经验,教会你平衡各种不同的因素,并与你的用户共同构建和谐健康的网络社区。 本书教会你 掌握创建任何网站时都会用到的原则 学习基本设计模式,以便向现有的网站中添加新的社交组件 学会在......一起来看看 《社交网站界面设计》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具