htmx表单+gin无法正确读取请求体

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

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

&lt;form hx-post=&quot;/addToDo&quot; hx-ext=&quot;json-enc&quot; hx-trigger=&quot;submit&quot; hx-target=&quot;#todos&quot; hx-swap=&quot;outerHTML&quot;&gt;
    &lt;input type=&quot;text&quot; placeholder=&quot;todo&quot; name=&quot;todo&quot; /&gt;
    &lt;input type=&quot;submit&quot; /&gt;
&lt;/form&gt;

&lt;ol id=&quot;todos&quot;&gt;&lt;/ol&gt;

The Gin server

r.POST(&quot;/addToDo&quot;, func(c *gin.Context) {
    fmt.Println(c.Request.Body)
    // ^this prints &quot;&amp;{0xc0000b2000 &lt;nil&gt; &lt;nil&gt; false true {0 0} false false false 0xeaee20}&quot;

    jsonData, err := ioutil.ReadAll(c.Request.Body)

    if err != nil {
        fmt.Println(&quot;something went wrong here boys&quot;)
        return
    }

    fmt.Println(jsonData)
    // ^this prints &quot;[116 111 100 111 61 104 101 108 108 111]&quot;
})

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:

https://pkg.go.dev/net/http#Request.FormValue

答案2

得分: 1

根据 @andrew-arrow 的指出,你的服务器接收到的是 x-www-form-urlencoded 格式的数据,而不是 JSON 编码的数据。这意味着 hx-ext=&quot;json-enc&quot; 并没有按预期工作。最有可能的原因是你忘记包含 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=&quot;json-enc&quot; 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):

&lt;script src=&quot;https://unpkg.com/htmx.org/dist/ext/json-enc.js&quot;&gt;&lt;/script&gt;

And gin has provided utils to bind JSON data (ShouldBindJSON). See the demo below:

package main

import (
	&quot;fmt&quot;

	&quot;github.com/gin-gonic/gin&quot;
)

var page = `&lt;html&gt;
  &lt;head&gt;
    &lt;script
      src=&quot;https://unpkg.com/htmx.org@1.9.2&quot;
      integrity=&quot;sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h&quot;
      crossorigin=&quot;anonymous&quot;
    &gt;&lt;/script&gt;
	&lt;script src=&quot;https://unpkg.com/htmx.org/dist/ext/json-enc.js&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;form
      hx-post=&quot;/addToDo&quot;
      hx-ext=&quot;json-enc&quot;
      hx-trigger=&quot;submit&quot;
      hx-target=&quot;#todos&quot;
      hx-swap=&quot;outerHTML&quot;
    &gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;todo&quot; name=&quot;todo&quot; /&gt;
      &lt;input type=&quot;submit&quot; /&gt;
    &lt;/form&gt;
    &lt;ol id=&quot;todos&quot;&gt;&lt;/ol&gt;
  &lt;/body&gt;
&lt;/html&gt;
`

func main() {
	r := gin.Default()
	r.GET(&quot;/&quot;, func(c *gin.Context) {
		_, err := c.Writer.WriteString(page)
		if err != nil {
			fmt.Println(err)
		}
	})
	r.POST(&quot;/addToDo&quot;, func(c *gin.Context) {
		var todo struct {
			Todo string `json:&quot;todo&quot;`
		}
		if err := c.ShouldBindJSON(&amp;todo); err != nil {
			fmt.Println(err)
			return
		}

		// Do not do this in production code because it&#39;s not safe against code injection.
		// Use the html/template package to generate the HTML fragment instead.
		if _, err := fmt.Fprintf(c.Writer, `&lt;ol id=&quot;todos&quot;&gt;&lt;li&gt;%s&lt;/li&gt;&lt;/ol&gt;`, 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:&quot;todo&quot; form:&quot;todo&quot;`
}
if err := c.ShouldBind(&amp;todo); err != nil {
	fmt.Println(err)
	return
}

huangapple
  • 本文由 发表于 2023年7月9日 04:41:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76644869.html
匿名

发表评论

匿名网友

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

确定