英文:
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论