Как создать универсальную функцию формы для from’ов с помощью gin (фреймворк) и golang?

#forms #go #interface #go-gin

#формы #Вперед #интерфейс #go-gin

Вопрос:

Я хочу создать функцию для обработки любых форм. Я хочу, чтобы он мог обрабатывать любые типы данных. Я пытаюсь использовать интерфейс для выполнения этой задачи.

 type Person struct {
    name        string 
    lastName    string
}


func HTMLForm(c *gin.Context) {
    var f Person
    c.ShouldBind(amp;f)
    c.JSON(http.StatusOK, f)
}

// with this function i get the correct info

// output: {"name":"john","lastName":"snow"}

func HTMLForm(c *gin.Context) {
    var f interface{}
    c.ShouldBind(amp;f)
    c.JSON(http.StatusOK, f)
}

// when i use the interface to make it usefull for any type of that
// i get null

// output: null

func HTMLForm(c *gin.Context) {
    var f interface{}
    ShouldBindJSON(f)
    c.JSON(http.StatusOK, f)
}

// output: null
  

Я хочу получить с интерфейсом тот же результат, который я получаю с типом данных «Person».

 // Another example of how i am using f

type Login struct {
    User     string 
    Password string 
}

func main() {
    router := gin.Default()

    router.POST("/loginForm", func(c *gin.Context) {
        var f interface{}

        if err := c.ShouldBind(amp;f); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, f)
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}

// output: null
  

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Обновить

Я хочу попытаться лучше объяснить мою проблему. Может быть, это обновление станет более понятным.

 // Golang code
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

// Binding from JSON
type Login struct {
    User     string `form:"user" json:"user" xml:"user"  binding:"required"`
    Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {
    router := gin.Default()

    router.LoadHTMLGlob("templates/*")

    router.GET("/login", GetLogin)
    router.POST("/loginJSON", PostJSONForm)
    router.POST("/loginXML", PostXMLForm)
    router.POST("/loginForm", PostHTMLForm)

    /*
        sudo lsof -n -i :8080
        kill -9 <PID>
    */
    router.Run(":8080")
}

func GetLogin(c *gin.Context) {
    c.HTML(http.StatusOK, "login.tmpl", nil)
}

// Example for binding JSON ({"user": "manu", "password": "123"})
// curl -v -X POST http://localhost:8080/loginJSON -H 'content-type: application/json' '{ "user": "manu", "password"="123" }'
func PostJSONForm(c *gin.Context) {
    //var json Login
    var json interface{}
    //var form map[string]interface{}

    if err := c.ShouldBindJSON(amp;json); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    /*
        if json.User != "manu" || json.Password != "123" {
            c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    */

    c.JSON(http.StatusOK, "json")
    c.JSON(http.StatusOK, json)
}

// Example for binding XML (
//  <?xml version="1.0" encoding="UTF-8"?>
//  <root>
//      <user>user</user>
//      <password>123</password>
//  </root>)
// curl -v -X POST http://localhost:8080/loginXML -H 'content-type: application/json' -d '{ "user": "manu", "password"="123" }'
func PostXMLForm(c *gin.Context) {
    //var xml Login
    var xml interface{}
    //var form map[string]interface{}

    if err := c.ShouldBindXML(amp;xml); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    /*
        if xml.User != "manu" || xml.Password != "123" {
            c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    */

    c.JSON(http.StatusOK, "xml")
    c.JSON(http.StatusOK, xml)
}

// Example for binding a HTML form (user=manuamp;password=123)
// curl -v -X POST http://localhost:8080/loginForm -H 'content-type: application/json' -d '{ "user": "manu", "password":"123" }'
func PostHTMLForm(c *gin.Context) {
    //var form Login
    var form interface{}
    //var form map[string]interface{}

    // This will infer what binder to use depending on the content-type header.
    if err := c.ShouldBind(amp;form); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    /*
        if form.User != "manu" || form.Password != "123" {
            c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    */

    c.JSON(http.StatusOK, "html")
    c.JSON(http.StatusOK, form)
}

//Template
<h1>Login</h1>
<form action="/loginForm" method="POST">
    <label>user:</label><br>
    <input type="text" name="user"><br>

    <label>password:</label><br>
    <input type="text" name="password"><br>

    <input type="submit">
</form>
  
  1. Я пробовал все эти разные варианты. Работает только один, я объясню подробнее ниже.

  2. Это работает идеально, если я использую «var form Login» вместо «var from interface{}». Но мне нужно, чтобы она могла работать с любым типом данных, поэтому мне нужно, чтобы она работала с интерфейсом {}.

  3. У меня был «успешный» вывод, только с одной из попыток interface {}:

$ curl -X POSThttp://localhost:8080/loginForm -H ‘content-type: application / json’ -d ‘{ «user»: «manu», «password»:»123″ }’

вывод: «html»{«password»: «123»,»user»: «manu»}

  1. Но когда я использую ее в HTML-форме, в браузере, с тем шаблоном, который я опубликовал, я получаю:

вывод: «html»null

  1. Я не уверен, что то, что я получаю (пункт 3), действительно является успешным результатом. Когда я использую Login var, он работает нормально, с curl и brower, вывод инвертируется:

$ curl -X POSThttp://localhost:8080/loginForm -H ‘content-type: application / json’ -d ‘{ «user»: «manu», «password»:»123″ }’

вывод: «html»{«user»: «manu»,»password»:»123″}

Это вся информация, которую я смог получить до сих пор.

Комментарии:

1. используйте map[string]interface{} вместо interface{}

Ответ №1:

Эта версия последнего ответа работала с отправкой html-формы и curl json.

 package main

import (
    "net/http"
    "fmt"

    "github.com/gin-gonic/gin"
)

// Binding from JSON
type Login struct {
    User     string `form:"user" json:"user" xml:"user"  binding:"required"`
    Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {
    router := gin.Default()

    router.LoadHTMLGlob("templates/*")

    router.GET("/login", func(c *gin.Context) {
        c.HTML(http.StatusOK, "login.tmpl", nil)
    })

    // Example for binding a HTML form (user=manuamp;password=123)
    router.POST("/loginForm", func(c *gin.Context) {
        var form Login

        // This will infer what binder to use depending on the content-type header.
        if err := c.ShouldBind(amp;form); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, form)
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}
  

И шаблоны/login.tmpl:

 <html>
<body>
<h1>Login</h1>
<form action="/loginForm" method="POST">
    <label>user:</label><br>
    <input type="text" name="user"><br>

    <label>password:</label><br>
    <input type="text" name="password"><br>

    <input type="submit">
</form>
</body>
</html>
  

Ответ №2:

Вы пытаетесь использовать ShouldBindJSON? Потому что он должен знать, каким типом вы хотите быть

ShouldBind проверяет тип содержимого, чтобы автоматически выбрать механизм привязки, в зависимости от заголовка «Content-Type» используются разные привязки:

из исходного кода gin:

 // ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (c *Context) ShouldBindJSON(obj interface{}) error {
    return c.ShouldBindWith(obj, binding.JSON)
}
  

Обновить:
В вашем случае это работает для меня?

 
router := gin.Default()

    router.POST("/loginForm", func(c *gin.Context) {
        var f interface{}

        if err := c.ShouldBindJSON(amp;f); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        log.Print(f)
        c.JSON(http.StatusOK, f)
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
  

Пример входа в систему

 curl -X POST -H "Content-Type: application/json" 
     --data '{"username":"xyz","password":"xyz"}'  localhost:8080/loginForm
  

Результат

 [GIN-debug] POST   /loginForm                --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
map[password:xyz username:xyz]
[GIN] 2019/04/23 - 10:07:00 | 200 |     239.317µs |             ::1 | POST     /loginForm


  

//////////////////////////////////

 package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

// Binding from JSON
type Login struct {
    User     string `form:"user" json:"user" xml:"user"  binding:"required"`
    Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {
    router := gin.Default()

    router.LoadHTMLGlob("templates/*")

    router.GET("/login", func(c *gin.Context) {
        c.HTML(http.StatusOK, "login.tmpl", nil)
    })

    // Example for binding a HTML form (user=manuamp;password=123)
    router.POST("/loginForm", func(c *gin.Context) {
        var form interface{}
        // This will infer what binder to use depending on the content-type header.
        if err := c.ShouldBind(amp;form); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, form)
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}
  

//шаблон

 <h1>Login</h1>
<form action="/loginForm" method="POST">
    <label>user:</label><br>
    <input type="text" name="user"><br>

    <label>password:</label><br>
    <input type="text" name="password"><br>

    <input type="submit">
</form>
  

// Я получаю null при использовании de HTML from, когда я пытаюсь выполнить команду curl, она работает

Комментарии:

1. Спасибо за комментарий. Я попробовал, и я получаю тот же результат.

2. функция HtmlForm(c * gin.Context) { var f interface { } ShouldBindJSON (f) c.JSON (http. StatusOK, f) } вывод: null

3. В вашем случае вы должны использовать c.ShouldBindJSON (f) ?

4. Я только что добавил еще один пример того, как я пытаюсь его использовать.

5. Я пробовал f и amp; f, но безуспешно.