英文:
htmx form + gin unable to properly read request body
问题
我有一个使用htmx的表单,尝试将第一个输入框的值发送到Gin服务器。
<form hx-post="/addToDo" hx-ext="json-enc" hx-trigger="submit" hx-target="#todos" hx-swap="outerHTML">
<input type="text" placeholder="todo" name="todo" />
<input type="submit" />
</form>
<ol id="todos"></ol>
Gin服务器的代码如下:
r.POST("/addToDo", func(c *gin.Context) {
fmt.Println(c.Request.Body)
// 这将打印出 "&{0xc0000b2000 <nil> <nil> false true {0 0} false false false 0xeaee20}"
jsonData, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
fmt.Println("something went wrong here boys")
return
}
fmt.Println(jsonData)
// 这将打印出 "[116 111 100 111 61 104 101 108 108 111]"
})
我考虑过在POST请求的URL中包含输入值作为参数,但我相当确定有一种方法可以在请求体中完成,只是我可能漏掉了某些东西。如何获取请求体或查询"todo"输入框的值?
英文:
I have this form using htmx, trying to send the input value of the first input to the Gin server
<form hx-post="/addToDo" hx-ext="json-enc" hx-trigger="submit" hx-target="#todos" hx-swap="outerHTML">
<input type="text" placeholder="todo" name="todo" />
<input type="submit" />
</form>
<ol id="todos"></ol>
The Gin server
r.POST("/addToDo", func(c *gin.Context) {
fmt.Println(c.Request.Body)
// ^this prints "&{0xc0000b2000 <nil> <nil> false true {0 0} false false false 0xeaee20}"
jsonData, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
fmt.Println("something went wrong here boys")
return
}
fmt.Println(jsonData)
// ^this prints "[116 111 100 111 61 104 101 108 108 111]"
})
I thought about having the post request url include the input value as a parameter but I'm fairly certain there's a way to do that in the request body and I'm just missing something. How do I get the request body or query the "todo" input?
答案1
得分: 1
我认为你只是将一个字符串作为[]byte获取。
a := []byte{116, 111, 100, 111, 61, 104, 101, 108, 108, 111}
fmt.Print(string(a))
todo=hello
我认为你不需要自己解析这个todo=hello
。你可以直接使用:
https://pkg.go.dev/net/http#Request.FormValue
英文:
I think you are just getting a string as a []byte.
a := []byte{116, 111, 100, 111, 61, 104, 101, 108, 108, 111}
fmt.Print(string(a))
todo=hello
I don't think you need to parse this todo=hello yourself. You can just use:
答案2
得分: 1
根据 @andrew-arrow 的指出,你的服务器接收到的是 x-www-form-urlencoded
格式的数据,而不是 JSON 编码的数据。这意味着 hx-ext="json-enc"
并没有按预期工作。最有可能的原因是你忘记包含 json-enc
扩展。请检查你的页面,确保它包含类似以下内容的代码(参见json-enc 扩展):
<script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>
而且 gin 提供了用于绑定 JSON 数据的实用工具(ShouldBindJSON)。请参考下面的示例:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
var page = `<html>
<head>
<script
src="https://unpkg.com/htmx.org@1.9.2"
integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js" crossorigin="anonymous"></script>
</head>
<body>
<form
hx-post="/addToDo"
hx-ext="json-enc"
hx-trigger="submit"
hx-target="#todos"
hx-swap="outerHTML"
>
<input type="text" placeholder="todo" name="todo" />
<input type="submit" />
</form>
<ol id="todos"></ol>
</body>
</html>`
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
_, err := c.Writer.WriteString(page)
if err != nil {
fmt.Println(err)
}
})
r.POST("/addToDo", func(c *gin.Context) {
var todo struct {
Todo string `json:"todo"`
}
if err := c.ShouldBindJSON(&todo); err != nil {
fmt.Println(err)
return
}
// Do not do this in production code because it's not safe against code injection.
// Use the html/template package to generate the HTML fragment instead.
if _, err := fmt.Fprintf(c.Writer, `<ol id="todos"><li>%s</li></ol>`, todo.Todo); err != nil {
fmt.Println(err)
}
})
_ = r.Run()
}
请注意,gin 的绑定功能可以检测到 Content-Type
头,并相应地选择绑定引擎。因此,你可以像这样编写代码,同时支持 JSON 编码和 x-www-form-urlencoded
格式的数据:
var todo struct {
Todo string `json:"todo" form:"todo"`
}
if err := c.ShouldBind(&todo); err != nil {
fmt.Println(err)
return
}
英文:
As pointed out by @andrew-arrow, your server gets x-www-form-urlencoded
data instead of JSON encoded data. That means hx-ext="json-enc"
does not work as expected. It's most likely because you forgot to include the json-enc
extension. Check your page to make sure it contains something like this (see The json-enc
Extension):
<script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>
And gin has provided utils to bind JSON data (ShouldBindJSON). See the demo below:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
var page = `<html>
<head>
<script
src="https://unpkg.com/htmx.org@1.9.2"
integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js" crossorigin="anonymous"></script>
</head>
<body>
<form
hx-post="/addToDo"
hx-ext="json-enc"
hx-trigger="submit"
hx-target="#todos"
hx-swap="outerHTML"
>
<input type="text" placeholder="todo" name="todo" />
<input type="submit" />
</form>
<ol id="todos"></ol>
</body>
</html>
`
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
_, err := c.Writer.WriteString(page)
if err != nil {
fmt.Println(err)
}
})
r.POST("/addToDo", func(c *gin.Context) {
var todo struct {
Todo string `json:"todo"`
}
if err := c.ShouldBindJSON(&todo); err != nil {
fmt.Println(err)
return
}
// Do not do this in production code because it's not safe against code injection.
// Use the html/template package to generate the HTML fragment instead.
if _, err := fmt.Fprintf(c.Writer, `<ol id="todos"><li>%s</li></ol>`, todo.Todo); err != nil {
fmt.Println(err)
}
})
_ = r.Run()
}
Please note that the binding feature of gin can detect the Content-Type
header and choose a binding engine accordingly. So you can write the code like this which supports both JSON encoded and x-www-form-urlencoded
data:
var todo struct {
Todo string `json:"todo" form:"todo"`
}
if err := c.ShouldBind(&todo); err != nil {
fmt.Println(err)
return
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论