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

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

Control field visibility depending on User role

问题

我想根据用户角色来隐藏/显示模型的某些字段。

实现这个功能最常用的方式是什么?

我不想创建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社区中最常见的做法是什么。我希望有一个干净和集中的(对于所有模型都相同的)解决方案,不需要产生大量重复的代码。

我之前尝试过的方法是:我只是尝试使用单独的模型来处理所有情况,并在它们之间进行类型转换。

英文:

I'd like to hide/show some fields of a model depending on User role.

What would be the most idiomatic way to implement it?

I don't really want to create N different types of the same model (where N is amount of User roles). Like:
UserEmployee, AdminEmployee, WhateverEmployee.

It would be perfect if there is some solution that uses the tags for it:

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"}

The question is really pretty broad. I just wanted to know how you handle this situation or what is the most common way to do it in the Go community. I want clean and centralized (same for all models) solution that won't require to produce tons of duplicated code.

What have I tried before: I've just tried to use separate models for all cases and cast between them.

答案1

得分: 4

  1. 创建一个空的结构体,用于保存过滤后的数据,结构体类型为Employee。
  2. 使用reflect包来比较字段标签是否包含所需的标签值(visibility role)。
  3. 当找到标签匹配时,将基本结构体的值复制到我们的过滤结构体中,并对输出结构体进行JSON编组。

代码如下:

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)

	// 遍历结构体中的每个字段
	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("使用 user 角色进行过滤:")
	fmt.Println(string(buser))

	bhr, err := json.MarshalIndent(fhr, "", "  ")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("\n使用 hr 角色进行过滤:")
	fmt.Println(string(bhr))

	badmin, err := json.MarshalIndent(fadmin, "", "  ")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("\n使用 admin 角色进行过滤:")
	fmt.Println(string(badmin))
}

输出结果为:

使用 user 角色进行过滤:
{
"id": "1",
"name": "Jack"
}
使用 hr 角色进行过滤:
{
"id": "1",
"name": "Jack",
"salary": 100000,
"boss": {
"id": "2",
"name": "John",
"salary": 120000,
"password": "pwd",
"rights": {
"create": true,
"update": true
}
}
}
使用 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上运行这段代码。

英文:
  1. Create an empty struct of your type (Employee in this problem) that will hold the filtered data.
  2. Use the reflect package to compare if the field tag contains the desired tag value (visibility role).
  3. Copy values of base struct to our filter struct when we find a tag match and json marshal the output struct:

package main
import (
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;reflect&quot;
&quot;strings&quot;
)
type Employee struct {
ID       string          `visibility:&quot;admin, hr, user&quot; json:&quot;id,omitempty&quot;`
Name     string          `visibility:&quot;admin, hr, user&quot; json:&quot;name,omitempty&quot;`
Salary   int             `visibility:&quot;admin, hr&quot; json:&quot;salary,omitempty&quot;`
Password string          `visibility:&quot;admin&quot; json:&quot;password,omitempty&quot;`
Rights   map[string]bool `visibility:&quot;admin&quot; json:&quot;rights,omitempty&quot;`
Boss     *Employee       `visibility:&quot;admin, hr&quot; json:&quot;boss,omitempty&quot;`
}
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 &lt; ev.NumField(); i++ {
v := ev.Field(i)
t := et.Field(i)
roles := t.Tag.Get(&quot;visibility&quot;)
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{
&quot;1&quot;,
&quot;Jack&quot;,
100000,
&quot;password321&quot;,
map[string]bool{&quot;create&quot;: false, &quot;update&quot;: false},
&amp;Employee{
&quot;2&quot;,
&quot;John&quot;,
120000,
&quot;pwd&quot;,
map[string]bool{&quot;create&quot;: true, &quot;update&quot;: true},
nil,
},
}
fuser := filterEmployee(e, &quot;user&quot;)
fhr := filterEmployee(e, &quot;hr&quot;)
fadmin := filterEmployee(e, &quot;admin&quot;)
buser, err := json.MarshalIndent(fuser, &quot;&quot;, &quot;  &quot;)
if err != nil {
fmt.Println(err)
}
fmt.Println(&quot;Filtering with role user: &quot;)
fmt.Println(string(buser))
bhr, err := json.MarshalIndent(fhr, &quot;&quot;, &quot;  &quot;)
if err != nil {
fmt.Println(err)
}
fmt.Println(&quot;\nFiltering with role hr: &quot;)
fmt.Println(string(bhr))
badmin, err := json.MarshalIndent(fadmin, &quot;&quot;, &quot;  &quot;)
if err != nil {
fmt.Println(err)
}
fmt.Println(&quot;\nFiltering with role admin: &quot;)
fmt.Println(string(badmin))
}

Output:
<!-- language: lang-none -->

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

Playground

EDIT: Updated answer for asker's request.

View the old playground for previous answer that ran into issues.

Old Playground

答案2

得分: 1

使用"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
}
....将其编组
}

或者你也可以在第一次将值添加到结构体中时不添加该值。详见文档

英文:

Use "omit empty"

type Employee struct {
ID string `json:&quot;,omitempty&quot;`
Name string `json:&quot;,omitempty&quot;`
Salary int `json:&quot;,omitempty&quot;`
}

Your function can look like

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

or you could also just not add the value to the struct in the first place. See the docs for more detail.

答案3

得分: 0

使用这个模块: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"}
}
英文:

use this module: https://github.com/icoom-lab/marian/

package main
import (
&quot;fmt&quot;
&quot;github.com/icoom-lab/marian&quot;
)
type Account struct {
Id   int    `json:&quot;id,omitempty&quot; role:&quot;admin&quot;`
Name string `json:&quot;name,omitempty&quot; role:&quot;admin,normal&quot;`
}
func main() {
account := Account{
Id:   1,
Name: &quot;Jhon&quot;,
}
fmt.Println(account)
// {id:1, Name:&quot;Jhon&quot;}
marian.CleanStruct(&quot;role&quot;, &quot;admin&quot;, &amp;account)
fmt.Println(account)
// {id:1, Name:&quot;Jhon&quot;}
marian.CleanStruct(&quot;role&quot;, &quot;normal&quot;, &amp;account)
fmt.Println(account)
// {Name:&quot;Jhon&quot;}
}

huangapple
  • 本文由 发表于 2017年3月16日 02:41:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/42818156.html
匿名

发表评论

匿名网友

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

确定