通过golang中的多个HTTP处理程序包含上下文对象 [英] Including context objects through multiple HTTP handlers in golang

查看:95
本文介绍了通过golang中的多个HTTP处理程序包含上下文对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚阅读 .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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆