Golang字段类型验证

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

Golang field type validation

问题

我有一个结构体:

type InputData struct {
   SomeNumber  int     `json:"someNumber"`
   SomeText    string  `json:"someText"`
}

我使用json.Unmarshal将我的HTTP请求体解析为类型为InputData的结构体。

如果我传入{"someNumber": "NaN", "someText": 42},我会得到类似以下的错误:

panic: json: cannot unmarshal string into Go struct field InputData.someNumber of type int

有没有办法获取完整且更结构化的错误数据?

例如,获取所有无法解析的字段列表以及原因?
(在我的例子中,我想知道someNumber是无效的,因为我传入了一个字符串,并且someText是无效的,因为我传入了一个数字)

我怀疑这是不可能的,但我仍然想在这个意义上验证我的输入。这是否适用于JSON-Schema验证?

英文:

I have a struct

type InputData struct {
   SomeNumber  int     `json:"someNumber"`
   SomeText    string  `json:"someText"`
}

I do json.Unmarshal my http request body into a struct of type InputData.

In case I pass in {"someNumber": "NaN", "someText": 42} I get something like

> panic: json: cannot unmarshal string into Go struct field
> InputData.someNumber of type int

Is there a way to get complete and more structured error data?

For instance, a list of all the non-parseable fields along w/ the reason for that?
(in my example, I'd like to know that someNumber is invalid because I passed in a string AND that someText is invalid because I passed in a number)

I doubt that's possible, but I'd still like to validate my input in that sense. Is it a use case for JSON-Schema validation?

答案1

得分: 3

是的,这可能是一个很好的使用 JSON Schema 验证的案例。你可以直接从你的 Go 结构体生成一个 JSON Schema,然后使用一个验证库来获取更有用的错误信息。例如,使用 Huma 的 JSON Schema 包和优秀的 xeipuuv/gojsonschema 库:

package main

import (
	"fmt"
	"reflect"

	"github.com/danielgtaylor/huma/schema"
	"github.com/xeipuuv/gojsonschema"
)

type InputData struct {
	SomeNumber int    `json:"someNumber"`
	SomeText   string `json:"someText"`
}

func main() {
	// 从结构体类型生成 JSON Schema。
	s, err := schema.Generate(reflect.TypeOf(InputData{}))
	if err != nil {
		panic(err)
	}

	// 从用户获取输入
	input := []byte(`{"someNumber": "NaN", "someText": 42}`)

	// 加载模式并将用户的输入作为字节进行验证,
	// 这意味着我们不需要先进行解组来处理无效的类型。
	loader := gojsonschema.NewGoLoader(s)
	doc := gojsonschema.NewBytesLoader(input)
	validator, err := gojsonschema.NewSchema(loader)
	if err != nil {
		panic(err)
	}
	result, err := validator.Validate(doc)
	if err != nil {
		panic(err)
	}

	// 显示结果!
	if !result.Valid() {
		for _, desc := range result.Errors() {
			fmt.Printf("%s (%s)\n", desc, desc.Value())
		}
		return
	}

	fmt.Println("输入有效!")
}

完整示例:https://go.dev/play/p/XEnrQswp_7N。输出结果如下:

someNumber: Invalid type. Expected: integer, given: string (NaN)
someText: Invalid type. Expected: string, given: integer (42)

ResultError 结构体包含了每个错误的大量信息。

英文:

Yes, this is probably a good use-case for JSON Schema validation. You can generate a JSON Schema directly from your Go struct and then use a validation library to get more useful errors. For example, using Huma's JSON Schema package with the excellent xeipuuv/gojsonschema:

package main

import (
	"fmt"
	"reflect"

	"github.com/danielgtaylor/huma/schema"
	"github.com/xeipuuv/gojsonschema"
)

type InputData struct {
	SomeNumber int    `json:"someNumber"`
	SomeText   string `json:"someText"`
}

func main() {
	// Generate a JSON Schema from the struct type.
	s, err := schema.Generate(reflect.TypeOf(InputData{}))
	if err != nil {
		panic(err)
	}

	// Get the input from the user
	input := []byte(`{"someNumber": "NaN", "someText": 42}`)

	// Load the schema and validate the user's input as bytes which
	// means we don't have to handle invalid types by first unmarshaling.
	loader := gojsonschema.NewGoLoader(s)
	doc := gojsonschema.NewBytesLoader(input)
	validator, err := gojsonschema.NewSchema(loader)
	if err != nil {
		panic(err)
	}
	result, err := validator.Validate(doc)
	if err != nil {
		panic(err)
	}

	// Display the results!
	if !result.Valid() {
		for _, desc := range result.Errors() {
			fmt.Printf("%s (%s)\n", desc, desc.Value())
		}
		return
	}

	fmt.Println("Input was valid!")
}

Full example: https://go.dev/play/p/XEnrQswp_7N. Output looks like:

someNumber: Invalid type. Expected: integer, given: string (NaN)
someText: Invalid type. Expected: string, given: integer (42)

The ResultError struct has a whole bunch of information about each error that occurs.

答案2

得分: 1

使用https://github.com/marrow16/valix(https://pkg.go.dev/github.com/marrow16/valix)可以通过以下方式实现:

package main

import (
	"fmt"
	"github.com/marrow16/valix"
	"testing"
)

type InputData struct {
	SomeNumber int    `json:"someNumber"`
	SomeText   string `json:"someText"`
}

var validator = valix.MustCompileValidatorFor(InputData{}, nil)

func TestInputDataValidation(t *testing.T) {
	myInputData := &InputData{}
	str := `{"someNumber": "NaN", "someText": 43}`
		    
	ok, violations, _ := validator.ValidateStringInto(str, myInputData)
	println("Validated OK? ", ok)
	if !ok {
		for i, v := range violations {
			fmt.Printf("Violation #%d: Property='%s', Message='%s'\n", i+1, v.Property, v.Message)
		}
	}
}

输出结果为:

Validated OK? false
Violation #1: Property='someNumber', Message='Value expected to be of type number'
Violation #2: Property='someText', Message='Value expected to be of type string'

声明:我是Valix的作者。

英文:

Using https://github.com/marrow16/valix (https://pkg.go.dev/github.com/marrow16/valix) it could be achieved with the following:

package main

import (
	"fmt"
	"github.com/marrow16/valix"
	"testing"
)

type InputData struct {
	SomeNumber int    `json:"someNumber"`
	SomeText   string `json:"someText"`
}

var validator = valix.MustCompileValidatorFor(InputData{}, nil)

func TestInputDataValidation(t *testing.T) {
	myInputData := &InputData{}
	str := `{"someNumber": "NaN", "someText": 43}`
	    
	ok, violations, _ := validator.ValidateStringInto(str, myInputData)
	println("Validated OK? ", ok)
	if !ok {
		for i, v := range violations {
			fmt.Printf("Violation #%d: Property='%s', Message='%s'\n", i+1, v.Property, v.Message)
		}
	}
}

Would output:

Validated OK? false
Violation #1: Property='someNumber', Message='Value expected to be of type number'
Violation #2: Property='someText', Message='Value expected to be of type string'

Disclosure: I am the author of Valix

huangapple
  • 本文由 发表于 2022年3月1日 23:58:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/71311230.html
匿名

发表评论

匿名网友

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

确定