【问题标题】:Is there a package to marshal in and out of x-www-form-urlencoding in golang是否有一个包可以在 golang 中编组进出 x-www-form-urlencoding
【发布时间】:2014-04-30 03:32:53
【问题描述】:

我想编组输入和输出 x-www-form-urlencoding,类似于使用 json 或 xml 的方法。是否有现有的软件包可以执行此操作,或者是否有任何文档说明如果不存在如何自己实施?

【问题讨论】:

  • 我相信,stdlib 中的 net/url 可以满足您的需求。请记住,有一个可靠的 http 客户端和服务器标准必须执行这些操作。
  • ParseQuery 返回一个 map[string][]string,这非常有用,但它不像 'encoding/json' 那样直接编组到结构。
  • 我不知道你从哪里得到这些信息。根据文档和我的使用经验,这是不正确的。
  • 如果您想就此争论,最好链接到您从中获取信息的文档。对我来说,我正在查看net/url.ParseQuery,它返回net/url.Values,它被定义为type Values map[string][]string。在此处查看更多信息:golang.org/pkg/net/url/#ParseQuery
  • 这使它成为 url.Values 类型,而不是 map[string][]string——您可以在它们之间进行转换,但它们不可互换。前者有一种方法可以做你想做的。

标签: go marshalling urlencode unmarshalling


【解决方案1】:

gorilla/schema 很受欢迎并且维护良好:

例如

func FormHandler(w http.RequestWriter, r *http.Request) {

    err := r.ParseForm()
    if err != nil {
         // handle error
    }
    person := new(Person) // Person being a struct type
    decoder := schema.NewDecoder()

    err = decoder.Decode(person, r.Form)
    if err != nil {
         // handle error
    }

}

goforms 也是一种选择。

2015 年 5 月 23 日更新:

  • gorilla/schema 仍然是我选择的最受支持的 map-to-struct 包之一,POST 表单值是一个常见的用例。
  • goji/param 也相当可靠,具有许多相同的功能。
  • mholt/binding 包含更多功能,但 (IMO) 以稍微复杂的 API 为代价。

我已经使用 gorilla/schema 几年了,没有遇到任何重大问题。我将它与vala 结合使用,在输入到达数据库之前验证输入(不是零、太短、太长等)。

【讨论】:

  • 太棒了,我没有看到 goforms,我喜欢有最小最大值和必填字段。我可能只是扩展 goforms 以包含一个实际的默认值。
  • 只想指出,gorilla 模式似乎没有得到很好的支持。 IE。即使自述文件说可以,也无法编组结构切片。有一张公开的票。
【解决方案2】:

我刚刚找到了https://github.com/ajg/form,这正是我想要的。还有https://github.com/gorilla/schema用于严格解码,https://github.com/google/go-querystring用于严格编码。

【讨论】:

    【解决方案3】:

    net/url 似乎处理得很好:

    package main
    
    import (
       "fmt"
       "net/url"
    )
    
    func main() {
       {
          m := url.Values{
             "CR": {"\r"}, "LF": {"\n"},
          }
          s := m.Encode()
          fmt.Println(s) // CR=%0D&LF=%0A
       }
       {
          s := "CR=%0D&LF=%0A"
          m, e := url.ParseQuery(s)
          if e != nil {
             panic(e)
          }
          fmt.Printf("%q\n", m) // map["CR":["\r"] "LF":["\n"]]
       }
    }
    

    【讨论】:

    • 我认为这应该是正确的答案,因为所有其他答案不仅仅是编组和解组数据(它们都将内容解析为预定义的结构,这很有用,但不是问的问题)。如果您只想编组/解组 url 参数中的所有数据,这应该是答案。
    【解决方案4】:

    https://github.com/google/go-querystring 不错,但不支持地图(和地图切片)。

    我开始 https://github.com/drewlesueur/querystring 寻求地图支持。 (它还不支持结构,但欢迎拉取请求)。

    【讨论】:

    • 看起来 go-querystring 只允许编码,不允许解码。您的项目看起来可以工作,但它不是惯用的,为什么不将这些函数命名为 Marshal 和 Unmarshal 并满足相同的接口?
    • 感谢您的反馈。我可以让我的更地道。我目前也只编码。
    【解决方案5】:

    The "github.com/pasztorpisti/qs" package 还可以对查询字符串和POST-ed 形式的结构进行编组/解组。

    例子:

    package main
    
    import "fmt"
    import "github.com/pasztorpisti/qs"
    
    type Query struct {
        Search     string
        Page       int
        PageSize   int
        Categories []string `qs:"category"`
    }
    
    func main() {
        queryStr, err := qs.Marshal(&Query{
            Search:     "my search",
            Page:       2,
            PageSize:   50,
            Categories: []string{"c1", "c2"},
        })
        fmt.Println("Marshal-Result:", queryStr, err)
    
        var q Query
        err = qs.Unmarshal(&q, queryStr)
        fmt.Println("Unmarshal-Result:", q, err)
    
        // Output:
        // Marshal-Result: category=c1&category=c2&page=2&page_size=50&search=my+search <nil>
        // Unmarshal-Result: {my search 2 50 [c1 c2]} <nil>
    }
    

    这个包的可能卖点:

    • 结构字段名称在查询字符串中自动显示为snake_case。为此,您不必将qs:"field_name" 添加到结构字段标记中。这种行为是可以改变的。
    • 模块化架构,即使是内置类型(如bool[]byte)也允许替换编组器和解组器,并在不修改类型本身的情况下添加对新类型的支持(例如:为@987654330 添加编组和/或解组@)。 Here 就是一个例子。
    • 您可以将req 选项添加到结构字段标记中,以便在解组时使该字段成为必需。请注意,这更像是验证而不是解组,golang 库通常将两者分开,但我发现这个选项非常有用,因为它经常被需要,它很简单并且读起来很好。对于那些希望避免req 并在解组后单独进行此验证的人:有一个nil 标签选项与默认opt(代表“可选”)几乎相同,只是它没有' t 当给定字段不在未编组查询字符串中时初始化 nil 指针。这样,验证器代码可以通过在解组后查找 nil 指针来检测缺失的字段。
    • qs 包可以在实际编组该类型的实例之前判断复杂类型(例如:您的结构)是否可编组。即使在编组给定类型的实例之后,大多数编组程序包也无法执行此操作!大多数封送处理程序通过遍历被封送处理的对象并仅检查访问的子对象的类型来检查复杂类型。这意味着如果复杂对象包含一个空容器(指针、映射或切片),则甚至不会检查容器的项目类型。在合同中qs 遍历复杂对象的类型结构(而不​​是对象本身),同时为类型创建封送器,因此它在必须失败的地方失败。考虑以下代码,其中标准 "encoding/json" 包成功地编组了一个类型包含不可编组类型的对象:

      package main
      
      import (
          "encoding/json"
          "fmt"
          "reflect"
      
          "github.com/pasztorpisti/qs"
      )
      
      type NonMarshalable func()
      
      func jsonEmptyMap() {
          // Since the container is empty "encoding/json" doesn't examine the type
          // of its items. This results in an unexpected success.
          var m = map[string]NonMarshalable{}
          j, err := json.Marshal(m)
          fmt.Println(string(j), err)
          // Output:
          // {} <nil>
      }
      
      func jsonNonEmptyMap() {
          var m = map[string]NonMarshalable{
              "f": func() {},
          }
          j, err := json.Marshal(m)
          fmt.Println(string(j), err)
          // Output:
          //  json: unsupported type: main.NonMarshalable
      }
      
      func qsEmptyMap() {
          // qs.Marshal fails even if the container is empty because the first step
          // of qs.Marshal fails: It can't create the marshaler object for this type.
          var m = map[string]NonMarshalable{}
          s, err := qs.Marshal(m)
          fmt.Println(s, err)
          // Output:
          //  error getting marshaler for map value type main.NonMarshalable :: unhandled type: main.NonMarshalable
      }
      
      func qsTypeCheck() {
          // You don't even have to try to marshal an object to find out whether its
          // type is marshal-friendly. You can check the type directly. By doing this
          // at startup (e.g.: from init functions) you can avoid delaying runtime
          // errors.
          t := reflect.TypeOf((map[string]NonMarshalable)(nil))
          err := qs.CheckMarshalType(t)
          fmt.Println(err)
          // Output:
          // error getting marshaler for map value type main.NonMarshalable :: unhandled type: main.NonMarshalable
      }
      
      func main() {
          jsonEmptyMap()
          jsonNonEmptyMap()
          qsEmptyMap()
          qsTypeCheck()
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-03
      • 2013-06-09
      • 1970-01-01
      • 2011-09-25
      • 2019-03-28
      • 2015-03-12
      • 2011-04-29
      相关资源
      最近更新 更多