通过golang中的多个HTTP处理程序包含上下文对象 [英] Including context objects through multiple HTTP handlers in golang
问题描述
我刚刚阅读 .ServeHTTP()方法来处理错误的详细信息。例如:
$ b $
类型appError结构{
错误错误
消息字符串
代码int
类型appHandler func(http.ResponseWriter,* http.Request)* appError
func(fn appHandler)ServeHTTP(w http.ResponseWriter,r * http .Request){
if e:= fn(w,r); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
func init(){
http.Handle( / view,appHandler(viewRecord))// viewRecord是一个appHandler函数
$ / code>
我喜欢这种方法,但我无法从概念上理解如何通过处理程序层包含上下文对象。例如:
func init(){
http.Handle(/ view,AuthHandler(appHandler(viewRecord )))
$ / code $ / pre>
AuthHandler
可能会创建一个& SessionToken {User:user}
对象并将其设置在 context.Context 对象。但我无法弄清楚如何将它们转换为 viewRecord
处理程序。想法?
解决方案我可以想到一些方法来做到这一点。
< h2>传递上下文
首先您可以更改签名以接受上下文
type appHandler func(http.ResponseWriter,* http.Request,context.Context)* appError
func(fn appHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
如果e:= fn(w,r,nil); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
现在我假定AuthHandler必须处理身份验证,并将用户设置为上下文对象。
您可以做的是创建另一个类型处理程序来设置上下文。像这样
type authHandler func(http.ResponseWriter,* http.Request,context.Context)* appError
func(fn authHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
//此处设置认证
uid:= 1
//设置您想要的方式的上下文
parent:= context.TODO()
ctx:= context.WithValue(parent,userIdKey,uid)
如果e:= fn(w,r,ctx ); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
通过这种方式,您可以通过以下方式使用它:
func init(){
http.Handle(/ view,appHandler(viewRecord))//不需要认证
http.Handle(/ viewAuth,authHandler(viewRecord))//需要认证
}
这是完整的代码
包主
导入(
fmt
净/ http
code.google.com/p /go.net/context
)
type appError struct {
错误错误
消息字符串
代码int
}
type key int
$ b $ const userIdKey key = 0
类型appHandler func(http.ResponseWriter,* http.Request,context.Context) * appError
func(fn appHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
if e:= fn(w,r,nil); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
type authHandler func(http.ResponseWriter,* http.Request,context .context)* appError
func(fn authHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
// setup setup here
uid:= 1
//以您想要的方式设置上下文
parent:= context.TODO()
ctx:= context.WithValue(parent,userIdKey,uid)
if e:= fn(w,r,ctx); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
func viewRecord(w http.ResponseWriter,r * http.Request, c context.Context)* appError {
if c == nil {
fmt.Fprintf(w,User not logged in)
} else {
uid:= c.Value(userIdKey)
fmt.Fprintf(w,用户使用uid登录:%d,uid)
}
返回nil
func init(){
http.Handle(/ view,appHandler(viewRecord))// viewRecord是一个appHandler函数
http.Handle( / viewAuth,authHandler(viewRecord))// viewRecord是一个authHandler函数
func main(){
http.ListenAndServe(:8080,nil)
code
创建地图上下文
不是传递上下文,而是创建
var contexts map [* http.Request] context.Context
并使用上下文[r]
获取视图
中的上下文。
但是由于map不是线程安全的,访问map必须用互斥锁保护。
和猜猜看是什么,这就是大猩猩背景为你做的事情,我认为这是更好的办法。
https://github.com/gorilla/context/blob/master/context.go#l20-28
这是完整的代码
包主
导入(
fmt
净/ http
github.com/gorilla/context
)
类型appError struct {
错误错误
消息字符串
代码int
}
类型键值
const userIdKey key = 0
类型appHandler func(http.ResponseWriter,* http.Request)* appError
func(fn appHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
if e:= fn(w,r); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
类型authHandler函数(http.ResponseWriter,* http.Request)* appError
$ b $ func(fn authHandler)ServeHTTP(w http.ResponseWriter,r * http.Request){
//此处设置认证
uid:= 1
context.Set(r,userIdKey,uid)
if e:= fn(w,r); e!= nil {// e是* appError,而不是os.Error。
http.Error(w,e.Message,e.Code)
}
}
func viewRecord(w http.ResponseWriter,r * http.Request) * appError {
if uid,ok:= context.GetOk(r,userIdKey); !ok {
fmt.Fprintf(w,用户未登录)
}其他{
fmt.Fprintf(w,用户使用uid登录:%d,uid )
}
return nil
}
func init(){
http.Handle(/ view,appHandler(viewRecord ))//不需要认证
http.Handle(/ viewAuth,authHandler(viewRecord))//需要认证
}
func main(){
http.ListenAndServe(:8080,零)
}
你可以也可以选择封装函数而不是类型函数进行验证
func AuthHandler(h appHandler)appHandler {
return func w http.ResponseWriter,r * http.Request)* appError {
// setup setup here
uid:= 1
context.Set(r,us erIdKey,uid)
return h(w,r)
}
}
func init(){
http.Handle(/ view ,appHandler(viewRecord))//不需要认证
http.Handle(/ viewAuth,appHandler(AuthHandler(viewRecord)))//需要认证
}
I just read this blog post about creating a function type and implementing the .ServeHTTP()
method on that function to be able to handle errors. For example:
type appError struct {
Error error
Message string
Code int
}
type appHandler func(http.ResponseWriter, *http.Request) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func init() {
http.Handle("/view", appHandler(viewRecord)) //viewRecord is an appHandler function
}
I like this approach but I can't conceptually figure out how to include a context object through handler layers. For example:
func init() {
http.Handle("/view", AuthHandler(appHandler(viewRecord)))
}
AuthHandler
would likely create a &SessionToken{User: user}
object and set that in a context.Context object for each request. I can't work out how to get that to the viewRecord
handler though. Ideas?
解决方案 I can think of a couple of approaches to do this.
Passing the context
first you can change the signature to accept context
type appHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r, nil); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
Now I assume the AuthHandler has to do with authentication and setup the user in the context object.
What you could do is create another type handler which setup the context. like this
type authHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
// setup the context the way you want
parent := context.TODO()
ctx := context.WithValue(parent, userIdKey, uid)
if e := fn(w, r, ctx); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
This way you can use it in the following way
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", authHandler(viewRecord)) // require authentication
}
This is the complete code
package main
import (
"fmt"
"net/http"
"code.google.com/p/go.net/context"
)
type appError struct {
Error error
Message string
Code int
}
type key int
const userIdKey key = 0
type appHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r, nil); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
type authHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
// setup the context the way you want
parent := context.TODO()
ctx := context.WithValue(parent, userIdKey, uid)
if e := fn(w, r, ctx); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func viewRecord(w http.ResponseWriter, r *http.Request, c context.Context) *appError {
if c == nil {
fmt.Fprintf(w, "User are not logged in")
} else {
uid := c.Value(userIdKey)
fmt.Fprintf(w, "User logged in with uid: %d", uid)
}
return nil
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // viewRecord is an appHandler function
http.Handle("/viewAuth", authHandler(viewRecord)) // viewRecord is an authHandler function
}
func main() {
http.ListenAndServe(":8080", nil)
}
create map context
Instead of passing the context, you create
var contexts map[*http.Request]context.Context
and get the context in view
with contexts[r]
.
But because of map is not thread safe, access to the map must be protected with mutex.
And guess what, this is what gorilla context is doing for you, and I think it's better approach
https://github.com/gorilla/context/blob/master/context.go#l20-28
this is the full code
package main
import (
"fmt"
"net/http"
"github.com/gorilla/context"
)
type appError struct {
Error error
Message string
Code int
}
type key int
const userIdKey key = 0
type appHandler func(http.ResponseWriter, *http.Request) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
type authHandler func(http.ResponseWriter, *http.Request) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
context.Set(r, userIdKey, uid)
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
if uid, ok := context.GetOk(r, userIdKey); !ok {
fmt.Fprintf(w, "User are not logged in")
} else {
fmt.Fprintf(w, "User logged in with uid: %d", uid)
}
return nil
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", authHandler(viewRecord)) // require authentication
}
func main() {
http.ListenAndServe(":8080", nil)
}
you can also opt for wrapper function instead of type function for auth
func AuthHandler(h appHandler) appHandler {
return func(w http.ResponseWriter, r *http.Request) *appError {
// setup authentication here
uid := 1
context.Set(r, userIdKey, uid)
return h(w, r)
}
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", appHandler(AuthHandler(viewRecord))) // require authentication
}
这篇关于通过golang中的多个HTTP处理程序包含上下文对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!