Как получить все параметры запроса GET request в структуру в Go?

Привет я хочу преобразовать параметры запроса get в структуру в Go, например, у меня есть эта структура:

type Filter struct {
    Offset int64  `json:"offset"`
    Limit  int64  `json:"limit"`
    SortBy string `json:"sortby"`
    Asc    bool   `json:"asc"`

    //User specific filters
    Username   string `json:"username"`
    First_Name string `json:"first_name"`
    Last_Name  string `json:"last_name"`
    Status     string `json:"status"`
}

и у меня есть дополнительные параметры, которые пользователь может указать при отправке запроса get, которые являются Offset, Limit, SortBy, Asc, Username, First_Name, Last_Name, Status.

если бы эти параметры были отправлены в теле, то я бы сделал это:

b, err := ioutil.ReadAll(r.Body)
if err != nil {

    log.WithFields(logFields).Errorf("Reading Body Message:failed:%v", err)

    return
}

var filter Filter
err = json.Unmarshal(b, &filter)

но я не могу отправить тело в GET запрос так что решение вместо того, чтобы получать каждый параметр отдельно, а затем помещать их в структуру?

1 ответов


использование гориллы schema пакета

на github.com/gorilla/schema пакет был изобретен именно для этого.

можно использовать теги структуры чтобы рассказать, как сопоставить параметры URL с полями структуры,schema пакет ищет "schema" ключи теге.

используя это:

import "github.com/gorilla/schema"

type Filter struct {
    Offset int64  `schema:"offset"`
    Limit  int64  `schema:"limit"`
    SortBy string `schema:"sortby"`
    Asc    bool   `schema:"asc"`

    //User specific filters
    Username   string `schema:"username"`
    First_Name string `schema:"first_name"`
    Last_Name  string `schema:"last_name"`
    Status     string `schema:"status"`
}

func MyHandler(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        // Handle error
    }

    filter := new(Filter)
    if err := schema.NewDecoder().Decode(filter, r.Form); err != nil {
        // Handle error
    }

    // Do something with filter
    fmt.Printf("%+v", filter)
}

маршалинг и unmarshaling с помощью json

отметим, что schema package также попытается преобразовать значения параметров в тип поля.

если структура будет содержать только поля []string тип (или вы готовы пойти на этот компромисс), вы можете сделать это без schema пакета.

Request.Form это map, картографическая от string to []string (как один параметр может быть указан несколько раз в URL:

Form url.Values

и url.Values:

type Values map[string][]string

так, например, если ваш Filter структура будет выглядеть это:

type Filter struct {
    Offset []string `json:"offset"`
    Limit  []string `json:"limit"`
    SortBy []string `json:"sortby"`
    // ..other fields
}

вы можете просто использовать json пакет маршалу r.Form, затем unmarshal его в вашу структуру:

func MyHandler(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        // Handle error
    }
    data, err := json.Marshal(r.Form)
    if err != nil {
        // Handle error
    }
    filter := new(Fiter)
    if err = json.Unmarshal(data, filter); err != nil {
        // Handle error
    }
    fmt.Printf("%+v", filter)
}

это решение обрабатывает, если для одного и того же имени параметра задано несколько значений. Если вы не заботитесь о нескольких значениях, и вы просто хотите один, вам сначала нужно "преобразовать"r.Form до map в одном string значения вместо []string.

вот как это может выглядеть например:

type Filter struct {
    Offset string `json:"offset"`
    Limit  string `json:"limit"`
    SortBy string `json:"sortby"`
    // ..other fields
}

// Transformation from map[string][]string to map[string]string:
m := map[string]string{}
for k, v := range r.Form {
    m[k] = v[0]
}

и тогда вы можете маршалом m и распаковать его в Filter struct таким же образом.