Go 中如何根据查询参数返回部分结构体?

问题描述 投票:0回答:2

我正在尝试根据查询参数实现休息资源上的属性选择。 API客户端将提供一个名为

fields
的查询参数。服务器将仅返回查询字符串中提到的资源的属性。服务器应根据查询参数返回资源的不同部分表示。以下是一些请求示例。

GET /api/person/42/?fields=id,createdAt
GET /api/person/42/?fields=address,account
GET /api/person/42/?fields=id,priority,address.city

我尝试走

map[string]any
路线,但进展并不顺利。我正在使用 MongoDB。当我将 mongo 文档解码为
map[string]any
时,字段名称和类型不匹配。因此我试图动态创建一个新的结构。

这是我的尝试:

func main() {
    query, _ := url.ParseQuery("fields=id,priority,address.city")
    fields := strings.Split(query.Get("fields"), ",") // TODO: extractFields
    person := getPerson() // Returns a Person Struct 
    personish := PartialStruct(person, fields)
    marshalled, _ := json.Marshal(personish) // TODO: err
    fmt.Println(string(marshalled))
}

func PartialStruct(original any, fields []string) any {
    // Is there any alternative to reflect ?
    originalType := reflect.TypeOf(original)
    partialFields := make([]reflect.StructField, 0)
    for _, field := range reflect.VisibleFields(originalType) {
        queryName := field.Tag.Get("json") // TODO: extractQueryName
        if slices.Contains(fields, queryName) {
            partialFields = append(partialFields, field)
        }
    }
    partialType := reflect.StructOf(partialFields)
    // Is there any alternative to Marshal/Unmarshal?
    partial := reflect.New(partialType).Interface()
    marshalled, _ := json.Marshal(original) // TODO: err
    _ = json.Unmarshal(marshalled, partial) // TODO: err
    return partial
}

这是一个可运行的示例https://go.dev/play/p/Egomxe5NjEc

资源被建模为嵌套结构。嵌套字段将用“.”表示。查询字符串中的点。

如何改进

PartialStruct
来处理
address.city
等嵌套字段?

如果有更好的方法我愿意改变方向。

json rest go struct query-string
2个回答
0
投票

查看第三方库:graphql

我写的一个例子可能对你有帮助:

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/graphql-go/graphql"
)

func main() {
    // Schema
    fields := graphql.Fields{
        "id": &graphql.Field{
            Type: graphql.ID,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                return 111, nil
            },
        },
        "priority": &graphql.Field{
            Type: graphql.String,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                return "admin", nil
            },
        },
        "address": &graphql.Field{
            Type: graphql.NewObject(graphql.ObjectConfig{
                Name: "address",
                Fields: graphql.Fields{
                    "city": &graphql.Field{
                        Type: graphql.String,
                    },
                    "country":  &graphql.Field{
                        Type: graphql.String,
                    },
                },
            }),
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                return map[string]string{
                    "city":"New York",
                    "country": "us",
                }, nil
            },
        },
    }
    rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields}
    schemaConfig := graphql.SchemaConfig{Query: graphql.NewObject(rootQuery)}
    schema, err := graphql.NewSchema(schemaConfig)
    if err != nil {
        log.Fatalf("failed to create new schema, error: %v", err)
    }

    // Query
    query := `
        {
            id,
            address {
                city,country
            },
            priority
        }
    `
    params := graphql.Params{Schema: schema, RequestString: query}
    r := graphql.Do(params)
    if len(r.Errors) > 0 {
        log.Fatalf("failed to execute graphql operation, errors: %+v", r.Errors)
    }
    rJSON, _ := json.Marshal(r)
    fmt.Printf("%s \n", rJSON)
}

这是一个可运行的示例 https://go.dev/play/p/pHH2iBzCLT-


0
投票

您正在寻找的库是 json-mask-go 它将处理编组后的部分响应

这是一个示例(另请注意将

address.city
更改为
address/city

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

    query, _ := url.ParseQuery("fields=id,priority,address/city")

    person := getPerson()
    marshalled, _ := json.Marshal(person) // TODO: err

    personish, err := jsonmask.Mask([]byte(marshalled), query.Get("fields"))
    if err != nil {
        fmt.Println("Error", err)
        return
    }

    fmt.Println(string(personish))
© www.soinside.com 2019 - 2024. All rights reserved.