根据用户角色控制字段可见性

Control field visibility depending on User role

我想 hide/show 模型的某些字段取决于用户角色。

最惯用的实现方式是什么?

我真的不想创建同一模型的 N 个不同类型(其中 N 是用户角色的数量)。像: UserEmployee、AdminEmployee、WhateverEmployee。

如果有一些使用标签的解决方案就完美了:

type Employee struct {
   ID string `visibility:"admin,user"`
   Name string `visibility:"admin,user"`
   Salary int `visibility:"admin"`
}

jsonBytes, _ := someLib.Marshal(Employee{"1", "John", 5000}, "user")

fmt.Println(string(jsonBytes)) // {"id":"1","name":"John"}

这个问题真的很广泛。我只是想知道您如何处理这种情况,或者在 Go 社区中最常见的处理方式是什么。我想要不需要产生大量重复代码的干净和集中(所有型号都相同)的解决方案。

我之前尝试过什么:我只是尝试对所有情况使用单独的模型并在它们之间进行转换。

使用"omit empty"

type Employee struct {
    ID string `json:",omitempty"`
    Name string `json:",omitempty"`
    Salary int `json:",omitempty"`
}

你的函数可以看起来像

func MarshallEmployee(e Employee, permission string) {
    if permission == "user"{
        e.Salary = 0
    }
    ....marshall it
}

或者您也可以一开始就不将值添加到结构中。有关详细信息,请参阅 the docs

  1. 创建一个您的类型(此问题中的 Employee)的空结构,用于保存过滤后的数据。
  2. 使用reflect包比较字段标签是否包含所需的标签值(可见性角色)。
  3. 当我们找到标签匹配和json marshal输出结构时,将基本结构的值复制到我们的过滤器结构:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "strings"
)

type Employee struct {
    ID       string          `visibility:"admin, hr, user" json:"id,omitempty"`
    Name     string          `visibility:"admin, hr, user" json:"name,omitempty"`
    Salary   int             `visibility:"admin, hr" json:"salary,omitempty"`
    Password string          `visibility:"admin" json:"password,omitempty"`
    Rights   map[string]bool `visibility:"admin" json:"rights,omitempty"`
    Boss     *Employee       `visibility:"admin, hr" json:"boss,omitempty"`
}

func filterEmployee(emp Employee, role string) Employee {
    var fEmployee Employee
    ev := reflect.ValueOf(emp)
    et := reflect.TypeOf(emp)

    // Iterate through each field within the struct
    for i := 0; i < ev.NumField(); i++ {
        v := ev.Field(i)
        t := et.Field(i)
        roles := t.Tag.Get("visibility")

        if strings.Contains(roles, role) {
            switch i {
            case 0: // ID
                fEmployee.ID = v.String()
            case 1: // Name
                fEmployee.Name = v.String()
            case 2: // Salary
                fEmployee.Salary = int(v.Int())
            case 3: // Password
                fEmployee.Password = v.String()
            case 4: // Rights
                fEmployee.Rights = v.Interface().(map[string]bool)
            case 5: // Boss
                fEmployee.Boss = v.Interface().(*Employee)
            }
        }
    }
    return fEmployee
}

func main() {

    e := Employee{
        "1",
        "Jack",
        100000,
        "password321",
        map[string]bool{"create": false, "update": false},
        &Employee{
            "2",
            "John",
            120000,
            "pwd",
            map[string]bool{"create": true, "update": true},
            nil,
        },
    }

    fuser := filterEmployee(e, "user")
    fhr := filterEmployee(e, "hr")
    fadmin := filterEmployee(e, "admin")

    buser, err := json.MarshalIndent(fuser, "", "  ")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Filtering with role user: ")
    fmt.Println(string(buser))

    bhr, err := json.MarshalIndent(fhr, "", "  ")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("\nFiltering with role hr: ")
    fmt.Println(string(bhr))

    badmin, err := json.MarshalIndent(fadmin, "", "  ")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("\nFiltering with role admin: ")
    fmt.Println(string(badmin))
}

输出:

Filtering with role user: 
{
  "id": "1",
  "name": "Jack"
}

Filtering with role hr: 
{
  "id": "1",
  "name": "Jack",
  "salary": 100000,
  "boss": {
    "id": "2",
    "name": "John",
    "salary": 120000,
    "password": "pwd",
    "rights": {
      "create": true,
      "update": true
    }
  }
}

Filtering with role admin: 
{
  "id": "1",
  "name": "Jack",
  "salary": 100000,
  "password": "password321",
  "rights": {
    "create": false,
    "update": false
  },
  "boss": {
    "id": "2",
    "name": "John",
    "salary": 120000,
    "password": "pwd",
    "rights": {
      "create": true,
      "update": true
    }
  }
}

Playground

编辑: 更新了提问者请求的答案。

查看旧版 playground 以获得 运行 问题的先前答案。

Old Playground

使用这个模块:https://github.com/icoom-lab/marian/

package main

import (
    "fmt"
    "github.com/icoom-lab/marian"
)

type Account struct {
    Id   int    `json:"id,omitempty" role:"admin"`
    Name string `json:"name,omitempty" role:"admin,normal"`
}

func main() {
    account := Account{
        Id:   1,
        Name: "Jhon",
    }

    fmt.Println(account)
    // {id:1, Name:"Jhon"}

    marian.CleanStruct("role", "admin", &account)
    fmt.Println(account)
    // {id:1, Name:"Jhon"}

    marian.CleanStruct("role", "normal", &account)
    fmt.Println(account)
    // {Name:"Jhon"}
}