在Golang中处理URL中的动态参数

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

Handle dynamic parameters in URL in Golang

问题

目前我正在使用Golang开发一个API Rest。我已经完成了对所有表进行CRUD操作的过程。现在有人要求我开发一个端点,根据URL中发送的参数在其中一个表中进行搜索。假设这是我用于该表的结构体:

type Media struct {
    ID                         uint       
    Key                        string     
    RecordKey                  string     
    RecordID                   string     
    SystemMediaKey             string          
    MediaObjectID              string   
    ChangedByID                string   
    ChangedByKey               string   
    MediaCategory              string   
    MimeType                   string   
    ShortDescription           string   
    LongDescription            string   
    EntryTimestamp             time.Time
    ModificationTimestamp      time.Time  
    DeletedAtTimestamp         *time.Time 
    MediaModificationTimestamp time.Time
    MediaURL                   string   
    MediaHTML                  string   
    Order                      int      
    Group                      string   
    Width                      int      
    Height                     int      
    ImageSize                  string   
    ResourceName               string  
    ClassName                  string 
    Permission                 *string
    MediaStatus                string
}

现在,他可以在URL中发送所有或部分字段,然后我需要将这些值分配给我的结构体,以便能够根据分配给对象的数据在数据库中进行搜索。

我正在使用Gorm处理与数据库的所有操作,使用gorilla/schema将值分配给POST请求,并使用Julien Schmidt Router。现在,我的问题是:

  1. 在路由中应该配置什么来接受动态参数?
  2. 如何将URL中的值分配给Media对象的属性?

谢谢!

英文:

currently am working on an API Rest in Golang. I have the process to CRUD all the tables. Now someone is asking me to develop an endpoint to search in one of that tables based on the parameters send in the URL. Let's say that this is my struct for that table:

type Media struct {
	ID                         uint       
	Key                   string     
	RecordKey           string     
	RecordID            string     
	SystemMediaKey      string          
	MediaObjectID       string   
	ChangedByID         string   
	ChangedByKey        string   
	MediaCategory              string   
	MimeType                   string   
	ShortDescription           string   
	LongDescription            string   
	EntryTimestamp             time.Time
	ModificationTimestamp      time.Time  
	DeletedAtTimestamp         *time.Time 
	MediaModificationTimestamp time.Time
	MediaURL                   string   
	MediaHTML                  string   
	Order                      int      
	Group                      string   
	Width                      int      
	Height                     int      
	ImageSize                  string   
	ResourceName               string  
	ClassName                  string 
	Permission                 *string
	MediaStatus                string
}

Now, he can send me all or some of that fields in the URL, the I need to assign that values to my struct to be able to search in the database based on the data assigned to the object.

I am using Gorm to handle everything with the database, gorilla/schema to assign the values on the POST requests and the Julien Schmidt Router. Now,my questions are:

  1. What should I configure in the route to accept dynamic parameters?
  2. How can I assign the values that comes in the URL to the a type Media object?
    Thank you!

答案1

得分: 1

你可以使用reflect包按名称迭代字段并设置它们。请注意,reflect包使用起来比较麻烦,并且如果使用不当可能会导致panic的危险。

另外,请注意url.Values.Get只返回第一个值(详见https://godoc.org/net/url#Values.Get)。

编辑:我添加了处理结构体中指针的代码。它们需要特殊处理。

package main

import (
	"fmt"
	"net/url"
	"reflect"
	"time"
)

type Media struct {
	ID                         uint
	Key                        string
	RecordKey                  string
	RecordID                   string
	SystemMediaKey             string
	MediaObjectID              string
	ChangedByID                string
	ChangedByKey               string
	MediaCategory              string
	MimeType                   string
	ShortDescription           string
	LongDescription            string
	EntryTimestamp             time.Time
	ModificationTimestamp      time.Time
	DeletedAtTimestamp         *time.Time
	MediaModificationTimestamp time.Time
	MediaURL                   string
	MediaHTML                  string
	Order                      int
	Group                      string
	Width                      int
	Height                     int
	ImageSize                  string
	ResourceName               string
	ClassName                  string
	Permission                 *string
	MediaStatus                string
}

func main() {

	testUrl := "www.example.com/test?MimeType=themimetype&Key=thekey&Permission=admin"

	u, err := url.Parse(testUrl)
	if err != nil {
		fmt.Println(err)
		return
	}

	params := u.Query()

	media := &Media{}

	val := reflect.ValueOf(media)

	for i := 0; i < val.Elem().NumField(); i++ {
		// get the reflect.StructField so we can get the Name
		f := val.Elem().Type().Field(i)

		// check if URL.Values contains the field
		if v := params.Get(f.Name); v != "" {
			// get the reflect.Value associated with the Field
			field := val.Elem().FieldByName(f.Name)

			kind := field.Kind()

			// you must switch for each reflect.Kind (associated with the type in your struct)
			// so you know which Set... method to call
			switch kind {
			case reflect.String:
				field.SetString(v)
			case reflect.Ptr:
				// pointers are a special case that must be handled manually unfortunately.
				// because they default to nil, calling Elem() won't reveal the underlying type
				// so you must simply string match the struct values that are pointers.
				switch f.Name {
				case "Permission":
					newVal := reflect.New(reflect.TypeOf(v))
					newVal.Elem().SetString(v)
					field.Set(newVal.Elem().Addr())
				case "DeletedAtTimestamp":
				}
			}

		}
	}

	fmt.Printf("%#v\n", media)
	fmt.Println(*media.Permission)
}

希望对你有帮助!

英文:

You could use the reflect package to iterate over the fields and set them by name. Be aware that the reflect package is arduous to use and comes with some dangers of panics if not used properly.

Also be aware that url.Values.Get only returns the first value (see https://godoc.org/net/url#Values.Get for details)

EDIT: I added code to account for the pointers in the struct. They are handled differently.

https://play.golang.org/p/AO4lYx7xka

package main
import (
&quot;fmt&quot;
&quot;net/url&quot;
&quot;reflect&quot;
&quot;time&quot;
)
type Media struct {
ID                         uint
Key                        string
RecordKey                  string
RecordID                   string
SystemMediaKey             string
MediaObjectID              string
ChangedByID                string
ChangedByKey               string
MediaCategory              string
MimeType                   string
ShortDescription           string
LongDescription            string
EntryTimestamp             time.Time
ModificationTimestamp      time.Time
DeletedAtTimestamp         *time.Time
MediaModificationTimestamp time.Time
MediaURL                   string
MediaHTML                  string
Order                      int
Group                      string
Width                      int
Height                     int
ImageSize                  string
ResourceName               string
ClassName                  string
Permission                 *string
MediaStatus                string
}
func main() {
testUrl := &quot;www.example.com/test?MimeType=themimetype&amp;Key=thekey&amp;Permission=admin&quot;
u, err := url.Parse(testUrl)
if err != nil {
fmt.Println(err)
return
}
params := u.Query()
media := &amp;Media{}
val := reflect.ValueOf(media)
for i := 0; i &lt; val.Elem().NumField(); i++ {
// get the reflect.StructField so we can get the Name
f := val.Elem().Type().Field(i)
// check if URL.Values contains the field
if v := params.Get(f.Name); v != &quot;&quot; {
// get the reflect.Value associated with the Field
field := val.Elem().FieldByName(f.Name)
kind := field.Kind()
// you must switch for each reflect.Kind (associated with the type in your struct)
// so you know which Set... method to call
switch kind {
case reflect.String:
field.SetString(v)
case reflect.Ptr:
// pointers are a special case that must be handled manually unfortunately.
// because they default to nil, calling Elem() won&#39;t reveal the underlying type
// so you must simply string match the struct values that are pointers.
switch f.Name {
case &quot;Permission&quot;:
newVal := reflect.New(reflect.TypeOf(v))
newVal.Elem().SetString(v)
field.Set(newVal.Elem().Addr())
case &quot;DeletedAtTimestamp&quot;:
}
}
}
}
fmt.Printf(&quot;%#v\n&quot;, media)
fmt.Println(*media.Permission)
}

答案2

得分: 0

我终于成功使用了这个库,它可以将一个map转换为struct。唯一的问题是,我需要对URL.Query() Values返回的map进行预处理,因为它对每个值返回一个数组,而我只需要值,而不是数组中的值。

英文:

I finally managed to use this library that converts a map to a struct. The only thing, is that I had to pre-process the map that is returned by URL.Query() Values because it returns an array for each value and I needed only the value and not inside an array.

huangapple
  • 本文由 发表于 2017年7月4日 04:58:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/44893792.html
匿名

发表评论

匿名网友

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

确定