Golang gorilla sessions在重定向后保留表单数据

huangapple go评论152阅读模式
英文:

Golang gorilla sessions preserving form data after redirect

问题

从逻辑的角度来看,我试图在重定向之间保留部分表单数据,以提供更好的用户体验,这样用户就不需要重新填写整个表单,只需填写无效的部分。

从编程的角度来看,我试图将request.PostForm数据结构保存在gorilla session的flashes中。在重定向后,我只能检索到类似于[0xc2001c8b10]的内存地址的字符串表示。

以下是在验证错误后保存flashes数据的部分(在此之前执行了request.ParseForm()):

session, _ := store.Get(request, "test")
session.AddFlash(err.Error(), "messages")
session.AddFlash(request.PostForm, "form_data")
session.Save(request, response)
http.Redirect(response, request, "/", http.StatusFound)
return

我还尝试使用gob注册结构,但没有效果:

func init() {
    gob.Register(&url.Values{})
}

表单值为小写,例如"first_name","last_name",如果这可能对此行为产生影响,请注意。

请记住,我成功地在重定向后检索到"messages",我遇到的唯一问题是结构化数据。

我是否做错了什么,或者是否有其他方法可以在重定向后填充部分表单,我不知道?

英文:

From logic point of view I am trying to preserve partial form data between redirects for better user experience so user won't have to fill entire form again, just the part that was invalid.

From programing point of view I am trying to save request.PostForm data structure in gorilla session's flashes. The only thing I manage to retrieve after redirect is string representation of memory address like this [0xc2001c8b10].

Here is the part where I save flashes data after validation error (request.ParseForm() was executed before this):

session, _ := store.Get(request, "test")
session.AddFlash(err.Error(), "messages")
session.AddFlash(request.PostForm, "form_data")
session.Save(request, response)
http.Redirect(response, request, "/", http.StatusFound)
return

Also I tried registering structure with gob without effect:

func init() {
    gob.Register(&url.Values{})
}

Form values are in lower case, eg. "first_name", "last_name" if that could have any influence on this behavior.

Please keep in mind that I successfully manage to retrieve "messages" after redirect, only problem I have is with structural data.

Am I doing something wrong or is there maybe another approach to fill partial forms after redirect that I am not aware of?

答案1

得分: 2

你的问题在于你正在使用类型为interface{}的值,这是一种通用类型,用于当存在多种类型时使用。这是gorilla的session.Flashes()方法的情况,因为它可以返回任意用户数据(无论你放入什么)。

你可以使用以下代码(在play上)重现你所遇到的问题:

type MyData struct {
    X int
}

// 模拟gorilla的Flashes()方法,它返回一个interface{}值的切片。
func Flashes() []interface{} {
    x := &MyData{2}

    // 将x转换为interface{}类型
    interfaceValue := interface{}(x)

    // 将转换后的x放入类型为[]interface{}的切片中
    return []interface{}{interfaceValue}
}

func main() {
    // 查看打印的[0xSOMETHING]
    fmt.Println("Some data:", Flashes())
}

运行此程序时,你将看到如下输出:

Some data: [0xc010000000]

这与你所遇到的情况相同。原因是fmt.Println不会遍历指针和接口的所有抽象级别,而是在某个级别停止,除非你告诉它打印所有内容。因此,如果你使用

fmt.Printf("Some data: %#v\n", Flashes())

你将确实看到你的数据:

Some data: []interface {}{(*main.MyData)(0xc010000000)}

要访问数据,你需要匹配所期望的类型的结果数据。你需要进行类型断言在play上的示例):

func main() {
    value := Flashes()[0]

    v, ok := value.(*MyData)
    
    if ok {
        fmt.Println("Some data:", v)
    } else {
        fmt.Println("Oh no, 存储的不是预期的内容")
    }
}

在上面的示例中,使用Flashes()返回的第一个闪存,并断言其为*MyData类型。如果确实是这种类型,则将其值打印到控制台。否则,将打印错误消息(尽管不是很好的错误消息)到控制台。
在断言一个变量是某种类型之后,断言的值就是断言的类型。也就是说,上面示例中的v*MyType类型。

英文:

Your problem is that you're working with values of type interface{}, which is the generic type
and used when there can be more than one type. This is the case for gorilla's session.Flashes()
method as it can return arbitrary user data (whatever you put in).

You can reproduce what you're experiencing with this code (on play):

type MyData struct {
	X int
}

// Simulate Flashes() from gorilla, which returns a slice of interface{} values.
func Flashes() []interface{} {
    x := &MyData{2}

	// Convert x to type interface{}
	interfaceValue := interface{}(x)

	// Put converted x into a slice of type []interface{}
	return []interface{}{interfaceValue}
}

func main() {
	// See that [0xSOMETHING] is printed
	fmt.Println("Some data:", Flashes())
}

When running this program you will see output like this:

> Some data: [0xc010000000]

This is the same you're experiencing. The reason for this is that fmt.Println does
not step through all levels of abstraction of pointers and interfaces and stops at
a certain level unless you tell it to print everything. So if you use

fmt.Printf("Some data: %#v\n", Flashes())

you will indeed see your data:

> Some data: []interface {}{(*main.MyData)(0xc010000000)}

What you have to do to access the data is to match the resulted data for the type
you're expecting. You have to do a type assertion (example on play):

func main() {
	value := Flashes()[0]

	v, ok := value.(*MyData)
	
	if ok {
		fmt.Println("Some data:", v)
	} else {
		fmt.Println("Oh no, there's something else stored than expected")
	}
}

In the example above the first flash returned by Flashes() is used and asserted to be
of type *MyData. If it is indeed this type, then it's value is printed to the console.
Otherwise an error message (albeit not a good one) is printed to the console.
After asserting a variable of being some type, the asserted value is of the asserted
type. That is the v in the example above is of type *MyType.

huangapple
  • 本文由 发表于 2013年11月5日 01:45:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/19773787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定