如何使用保存在控制器中访问的中间件中的上下文中的“类型结构”

This is my first question regarding GoLang please be gentle. I am building a test JSON API.

I have a type struct named User

type User struct {
    UserID              int
    Email               string
    FirstName           string
    LastName            string
    PasswordHash        string
}

In middleware, I verify JWT token is valid and if so, I load entire record of User and save it in the context so that it is available in handlers under.

So in middleware, I save User struct like this

    context.Set(r, "User",  *User)
    fmt.Println(*User)

    next.ServeHTTP(w, r)

Output of Println is

{12 sallu@domain.com.au Mr. Sallu $2a$14oUg3d1rm./.B/vUyhYR9/hlHfagE4tGicNc14EWK3u }

Then in the final handler I access this context and get User object.

User    := context.Get(r, "User")

fmt.Println("---User---", fmt.Sprintf(" %T ", User))
fmt.Println(User)

Output of Println is

---User---  models.User
{12 sallu@domain.com.au Mr. Sallu $2a$14oUg3d1rm./.B/vUyhYR9/hlHfagE4tGicNc14EWK3u }

The issue is that in Controller when I access User.Email I am getting an error

User.Email undefined (type interface {} is interface with no methods)

How can I fix this..? I want to use User.Email or User.UserID. Seems like context is doing something to it.

Please help

Everywhere in your code please use different names for variable and type.

e.g. keep type User and variable user.

In your controller, type assert your user back to User.

if u, ok := user.(User); ok {
    fmt.Println(u.Email)
}

https://tour.golang.org/methods/15

User.Email is a little ambiguous there. You're using the same name, User for both the type definition and a variable. However, the error message you're seeing:

User.Email undefined (type interface {} is interface with no methods)

shows that the compiler is understanding User there to be the variable, because it thinks the type is interface{} -- the generic type in Go.

And the error message you're seeing is slightly different from what I get if I don't use a variable at all, but just try to access a method that doesn't exist on a type, e.g.:

package main

import(
    "fmt"
)

type User struct {
    Email           string
}

func main() {
    fmt.Println(User.Email)
}

When executed prints this error:

User.Email undefined (type User has no method Email)

Still, it would be best if you changed your variable name to be different from your type name, e.g.

u := context.Get(r, "User")

And then you would access u.Email

Given that the compiler is seeing your variable as type interface{} rather than type User, biosckon may be correct that you are missing a type assertion after you extracted from context. See the go blog on context, especially:

FromContext extracts a userIP from a Context:

func FromContext(ctx context.Context) (net.IP, bool) {
    // ctx.Value returns nil if ctx has no value for the key;
    // the net.IP type assertion returns ok=false for nil.
    userIP, ok := ctx.Value(userIPKey).(net.IP)
    return userIP, ok
}

See also this Stackoverflow question and answer on fetching values from context, especially:

You need to set and extract the value with the correct type as well...I often times define helper functions to extract the context values and do the type assertion

What's strange about that is the output of your print statements, which make it look like you have the right type. But again that's a little ambiguous since you're using the same name for a variable and a type. And -- it doesn't look like you shared all your code -- are those prints from the scope where you first set them rather than after extracting from context in a different scope? If so, that would explain the difference.