将URL.Query(切片的映射)转换为golang结构体。

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

Convert URL.Query (map of slices) to struct golang

问题

从标准库的URL.Query()到一个结构体的直接映射会很棒。

Query() 返回一个类似于以下的映射:
map[a:[aaaa] b:[bbbb] c:[cccc]]

结构体如下所示:

type Thing struct {
    A    string
    B    string
    C    string
}
  • 我不知道为什么URL.Query返回一个包含数组元素的映射(嗯...我知道为什么,但是一个GET请求不太可能有重复的参数)
英文:

It would be awesome to have a straight forward mapping from the standard library URL.Query() to an struct.

Query() returns a map like:
map[a:[aaaa] b:[bbbb] c:[cccc]]

The struct looks like:

type Thing struct {
    A    string
    B    string
    C    string
}
  • I've no idea why URL.Query returns a map with array elements inside tough. (well.. I know why but a GET is not likely to have duplicated params)

答案1

得分: 14

请查看以下完整示例,该示例演示了如何直接在golang结构体中解析GET查询参数,然后将结构体作为响应发送回去。

package main

import (
	"log"
	"net/http"
	"encoding/json"
	"github.com/gorilla/schema"
)

var decoder = schema.NewDecoder()

type EmployeeStruct struct {
	MemberId         string `schema:"memberId"`
	ActivityType     string `schema:"activityType"`
	BusinessUnitCode int    `schema:"businessUnitCode"`
}

func GetEmployee(w http.ResponseWriter, r *http.Request) {
	var employeeStruct EmployeeStruct

	err := decoder.Decode(&employeeStruct, r.URL.Query())
	if err != nil {
		log.Println("Error in GET parameters: ", err)
	} else {
		log.Println("GET parameters: ", employeeStruct)
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(employeeStruct)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/GetEmployee", GetEmployee)
	log.Fatal(http.ListenAndServe(":8080", mux))
}

执行和测试步骤(假设你将上述代码保存为employee.go):

步骤1:运行命令 go run employee.go

步骤2:在浏览器中打开链接 http://localhost:8080/GetEmployee?memberId=123&activityType=Call&businessUnitCode=56

步骤3:你应该在浏览器窗口中看到以下响应:

{
    "MemberId": "123",
    "ActivityType": "Call",
    "BusinessUnitCode": 56
}

步骤4:在控制台上你应该看到以下输出:

GET parameters: {123 Call 56}
英文:

Please find below the complete example of parsing get query params directly in a golang struct and then sending the struct back as response

package main

import (
	"log"
	"net/http"
	"encoding/json"
	"github.com/gorilla/schema"
)

var decoder  = schema.NewDecoder()

type EmployeeStruct struct {
	MemberId         string `schema:"memberId"`
	ActivityType     string `schema:"activityType"`
	BusinessUnitCode int    `schema:"businessUnitCode"`
}

func GetEmployee(w http.ResponseWriter, r *http.Request) {
    var employeeStruct EmployeeStruct
    
    err := decoder.Decode(&employeeStruct, r.URL.Query())
	if err != nil {
		log.Println("Error in GET parameters : ", err)
	} else {
		log.Println("GET parameters : ", employeeStruct)
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(employeeStruct)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/GetEmployee", GetEmployee)
	log.Fatal(http.ListenAndServe(":8080", mux))
}

Steps to execute & Test (Assuming you are saving above code in employee.go) :

Step 1 : go run employee.go

Step 2 : Open in browser http://localhost:8080/GetEmployee?memberId=123&activityType=Call&businessUnitCode=56

Step 3 : You should get below response in browser window

{
    "MemberId": "123",
    "ActivityType": "Call",
    "BusinessUnitCode": 56
}

Step 4 : On console you should see below

GET parameters :  {123 Call 56}

答案2

得分: 3

示例:

filters={"reference":["docker.io/library/alpine:latest"]}

需要进行URL编码:

filters=%7B%22reference%22%3A%5B%22docker.io%2Flibrary%2Falpine%3Alatest%22%5D%7D

并且可以使用"github.com/gorilla/schema"

query := struct {
	All     bool
	Filters map[string][]string `schema:"filters"`
	Digests bool
	Filter  string 
}{}
decoder := schema.NewDecoder()
decoder.Decode(&query, r.URL.Query())
英文:

example:

filters={"reference":["docker.io/library/alpine:latest"]}

need url encode to:

filters=%7B%22reference%22%3A%5B%22docker.io%2Flibrary%2Falpine%3Alatest%22%5D%7D

and could use "github.com/gorilla/schema"

	query := struct {
		All     bool
		Filters map[string][]string `schema:"filters"`
		Digests bool
		Filter  string 
	}{}
	decoder := schema.NewDecoder()
	decoder.Decode(&query, r.URL.Query())

答案3

得分: 2

如@mh-cbon所指出的,gorilla schema是这里的终极解决方案。

而不是从URL属性中获取queryParams。

func handleRequest(w http.ResponseWriter, r *http.Request) {
    queryString := r.URL.Query()
    //...解析Values -> map[string][]string
}

gorilla schema的方法是将r.PostForm传递给解码函数。

func handleRequest(w http.ResponseWriter, r *http.Request) {
    err := decoder.Decode(person, r.PostForm)
    //...使用反射可以调用每个结构体的属性,使用PostForm(url string, data url.Values)签名
    
    fmt.Print(person.GoodJobGorilla)
}
英文:

As pointed out by @mh-cbon gorilla schema is the ultimate solution here.

Instead for obtaining the queryParams from the URL attribute.

func handleRequest(w http.ResponseWriter, r *http.Request) {
    queryString := r.URL.Query()
    //...parsing the Values -> map[string][]string
}

The approach of gorilla schema is to ship r.PostForm to the decode function.

func handleRequest(w http.ResponseWriter, r *http.Request) {
    err := decoder.Decode(person, r.PostForm)
    //...using reflect each struct's property can be called using 
    // the PostForm(url string, data url.Values) signature
    
    fmt.Print(person.GoodJobGorilla)
}

答案4

得分: 1

使用ggicci/httpin

免责声明:我是这个包的创建者和维护者。

httpin帮助您轻松解码HTTP请求数据,包括:

  • 查询参数,例如?name=john&is_member=true
  • 头部信息,例如Authorization: xxx
  • 表单数据,例如username=john&password=******
  • JSON/XML Body,例如POST {"name":"john"}
  • 路径变量,例如/users/{username}
  • 文件上传

如何使用?

type ListUsersInput struct {
	Page     int  `in:"query=page"`
	PerPage  int  `in:"query=per_page"`
	IsMember bool `in:"query=is_member"`
}

func ListUsers(rw http.ResponseWriter, r *http.Request) {
	input := r.Context().Value(httpin.Input).(*ListUsersInput)

	if input.IsMember {
		// Do sth.
	}
	// Do sth.
}

httpin具有以下特点:

  • 文档完善:请参阅https://ggicci.github.io/httpin/
  • 经过充分测试:覆盖率超过98%
  • 开放集成:与net/httpgo-chi/chigorilla/muxgin-gonic/gin等集成
  • 可扩展(高级功能):通过添加自定义指令。详细信息请阅读httpin - custom directives
  • 被提及的很棒:https://github.com/avelino/awesome-go#forms
英文:

Using ggicci/httpin

Disclaimer: I'm the creator and maintainer of this package.

httpin helps you easily decoding HTTP request data from

  • Query parameters, e.g. ?name=john&is_member=true
  • Headers, e.g. Authorization: xxx
  • Form data, e.g. username=john&password=******
  • JSON/XML Body, e.g. POST {"name":"john"}
  • Path variables, e.g. /users/{username}
  • File uploads

How to use?

type ListUsersInput struct {
	Page     int  `in:"query=page"`
	PerPage  int  `in:"query=per_page"`
	IsMember bool `in:"query=is_member"`
}

func ListUsers(rw http.ResponseWriter, r *http.Request) {
	input := r.Context().Value(httpin.Input).(*ListUsersInput)

	if input.IsMember {
		// Do sth.
	}
	// Do sth.
}

httpin is:

答案5

得分: 1

只需将字符串解析为URL,然后可以使用github.com/gorilla/schema库进行解析 将URL.Query(切片的映射)转换为golang结构体。

// 解析查询字符串到结构体的示例
package main

import (
	"log"
	"net/url"

	"github.com/gorilla/schema"
)

type URLParams struct {
	Code  string `schema:"code"`
	State string `schema:"state"`
}

func main() {
	var (
		params  URLParams
		decoder = schema.NewDecoder()
	)
	p := "https://www.redirect-url.com?code=CODE&state=RANDOM_ID"

	u, _ := url.Parse(p)

	err := decoder.Decode(&params, u.Query())
	if err != nil {
		log.Println("解码参数时出错:", err)
	} else {
		log.Printf("解码后的参数:%#v\n", params)
	}
}

https://go.dev/play/p/CmuPhdKh6Yg

英文:

Just parse the string to URL and after you can use the lib github.com/gorilla/schema to parse it 将URL.Query(切片的映射)转换为golang结构体。

// Example to parse querystring to struct
package main

import (
	"log"
	"net/url"

	"github.com/gorilla/schema"
)

type URLParams struct {
	Code  string `schema:"code"`
	State string `schema:"state"`
}

func main() {
	var (
		params  URLParams
		decoder = schema.NewDecoder()
	)
	p := "https://www.redirect-url.com?code=CODE&state=RANDOM_ID"

	u, _ := url.Parse(p)

	err := decoder.Decode(&params, u.Query())
	if err != nil {
		log.Println("Error in Decode parameters : ", err)
	} else {
		log.Printf("Decoded parameters : %#v\n", params)
	}
}

https://go.dev/play/p/CmuPhdKh6Yg

答案6

得分: 1

如果有人正在使用Echoquery结构标签将对这种情况非常有用。

示例结构体

type struct Name {
    FirstName string `query:"first_name"`
    LastName string `query:"last_name"`
}

示例查询参数

?first_name="shahriar"&last_name="sazid"

代码

var name Name
err := c.Bind(&name); if err != nil {
    return c.String(http.StatusBadRequest, "bad request")
}
英文:

If anyone is using Echo, query struct tag will be useful for this case.

Example Struct

type struct Name {
    FirstName string `query:"first_name"`
    LastName string `query:"last_name"`
}

Example Query Param

?first_name="shahriar"&last_name="sazid"

Code

var name Name
err := c.Bind(&name); if err != nil {
    return c.String(http.StatusBadRequest, "bad request")
}

答案7

得分: 0

你可以使用Echo的graceful包。

我写了一些代码作为示例,并添加了自解释的注释

package main

import (
    "log"
    "github.com/labstack/echo"
)

// 使用form:""标签声明你的结构体
type Employee struct {
    MemberId         string `form:"memberId"`
    ActivityType     string `form:"activityType"`
    BusinessUnitCode int    `form:"businessUnitCode"`
}

// 你的处理函数应该像这个方法一样
// 接收一个echo.Context并返回一个错误
func GetEmployee(ctx echo.Context) error {
    var employee Employee
    // 使用Bind方法,你可以从echo.Context中获取HTTP请求的Post Body或查询参数
    if err := ctx.Bind(&employee); err != nil {
        return err
    }

    // 现在你可以使用你的结构体,例如
    return ctx.JSON(200, employee.MemberId)
}

// 现在在你的main函数或任何需要的地方使用处理函数
func main() {
    e := echo.New()
    e.GET("/employee", GetEmployee)
    log.Fatal(e.Start(":8080"))
}
英文:

You can use the graceful package of Echo.

I write some codes as an example, with self-explanatory comments

 package main

 import (
     "log"
     "github.com/labstacks/echo"
)

// Declare your struct with form: "" tag
type Employee struct {
    MemberId         string `form:"memberId"`
    ActivityType     string `form:"activityType"`
    BusinessUnitCode int    `form:"businessUnitCode"`
}

// Your handlers should look like this method 
// Which takes an echo.Context and returns an error
func GetEmployee(ctx echo.Context) error{
    var employee Employee
    // With Bind, you can get the Post Body or query params from http.Request
    // that is wrapped by echo.Context here 
    if err := ctx.Bind(&employee);err != nil {
        return err
    } 

    // now you can use your struct , e.g
    return ctx.json(200, employee.MemberId)
}

// now use the handler in your main function or anywhere you need
func main() {
    e := echo.New()
    e.Get("/employee", GetEmployee)
    log.Fatal(e.Start(":8080"))
}

huangapple
  • 本文由 发表于 2016年11月13日 00:11:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/40564842.html
匿名

发表评论

匿名网友

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

确定