go-zero icon indicating copy to clipboard operation
go-zero copied to clipboard

I am planning to implement Auto Validation

Open Linde7777 opened this issue 1 year ago • 14 comments
trafficstars

Related issue(but stale): #3167

I am planning to implement such feature.

Is your feature request related to a problem? Please describe. Current auto validation rules is not enough.

Describe the solution you'd like Imitating Gin's validator implementation. There is a tag "binding", once you add that tag to the struct, when you call Bind()-like function(in go-zero, it is httpx.Parse()), it will automatically validate the struct's fields. Gin underlying using the go-playground's validator, it has many validation rules, and also support custom validation rule.

Describe alternatives you've considered Currently I have no alternatives for auto validation

Additional context In Gin's implementation, the validation's interface has such function: Validate(obj any) error, and in go-zero, the validation interface's function is Validate() error, I not sure which I should use

Linde7777 avatar Jun 20 '24 08:06 Linde7777

reference:

func init() {
	uni := ut.New(zh.New())
	trans, _ := uni.GetTranslator("zh")

	v := validator.New()
	logx.Must(zh_translations.RegisterDefaultTranslations(v, trans))

	httpx.SetValidator(&validate{validate: v})
}

type validate struct {
	validate *validator.Validate
}

func (v *validate) Validate(r *http.Request, data any) error {
	return v.validate.StructCtx(r.Context(), data)
}

chenquan avatar Jun 27 '24 01:06 chenquan

Is the code you provided a suggestion for implementation?

I noticed that there are two interface:

// [email protected]/rest/httpx/requests.go 

// Validator defines the interface for validating the request.
type Validator interface {
	// Validate validates the request and parsed data.
	Validate(r *http.Request, data any) error
}
// [email protected]/core/validation/validator.go

// Validator represents a validator.
type Validator interface {
	// Validate validates the value.
	Validate() error
}

I’m curious about why there are two different interfaces. Could you please explain?

Linde7777 avatar Jun 27 '24 03:06 Linde7777

Yes, just choose one of the two.

https://github.com/zeromicro/go-zero/blob/01bbc78baca219655b9618ca5b6df70c9662a29b/rest/httpx/requests.go#L55-L59

chenquan avatar Jun 27 '24 03:06 chenquan

https://github.com/zeromicro/go-zero/blob/01bbc78baca219655b9618ca5b6df70c9662a29b/rest/httpx/requests.go#L55-L59

https://github.com/zeromicro/go-zero/blob/01bbc78baca219655b9618ca5b6df70c9662a29b/rest/httpx/requests.go#L31-L35

question 1 I am confused about the design of the interface Validator, I think the validating functionality is not related to the http.Request, it only related to the struct

question 2 I am thinking about the lazy initlization of validator, so maybe we shouldn't write validator.Load(), we just need to call validator.Validate(), in the Validate(), it will call lazyinit(), which will be implemented by sync.Once

Linde7777 avatar Jul 01 '24 15:07 Linde7777

  1. It may be necessary to verify the path or header content.
  2. Show your code and discuss it further.

chenquan avatar Jul 02 '24 00:07 chenquan

It may be necessary to verify the path or header content.

For a struct, you can use a tag to register validation rules:

type SignUpReq struct{
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required,complexpassword"`
}

But what would be the way to register a validation rule for path or header?

Linde7777 avatar Jul 02 '24 04:07 Linde7777

Show your code and discuss it further.

https://github.com/Linde7777/go-zero/tree/auto_validation/rest/httpx

I have finished the part of validating struct and the lazy initialization

Linde7777 avatar Jul 02 '24 04:07 Linde7777

It may be necessary to verify the path or header content.

For a struct, you can use a tag to register validation rules:

type SignUpReq struct{
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required,complexpassword"`
}

But what would be the way to register a validation rule for path or header?

for example: https://github.com/zeromicro/go-zero/blob/4a62d084a93fb50cb85ca62bc8620b041cdc1a4a/rest/httpx/requests_test.go#L509-L514

chenquan avatar Jul 02 '24 09:07 chenquan

Show your code and discuss it further.

https://github.com/Linde7777/go-zero/tree/auto_validation/rest/httpx

I have finished the part of validating struct and the lazy initialization

Yes, you can.

chenquan avatar Jul 02 '24 09:07 chenquan

https://github.com/zeromicro/go-zero/blob/4a62d084a93fb50cb85ca62bc8620b041cdc1a4a/rest/httpx/requests_test.go#L509-L514

It seems that the code you provide didn't answer "how to register validation rules for http.Request?"

For register validation rule of http.Request, I have figured out two method:

method 1: define the validation rule for http.Request in the struct

type SignUpReq struct{
	Email string `json:"email" binding:"required,email" rv:"xxrequestvalidationrule"`
	Password string `json:"password" binding:"required,complexpassword"`
}

The tag "rv" is for "Request Validation". The user need to define the rule in the first field of struct.

method 2: using a middleware

We don't need to validate request in the interface Validator

Linde7777 avatar Jul 02 '24 10:07 Linde7777

go-zero provides validation.Validator interface and validator Validator to help users customize validation capabilities, you can choose according to the business background and personal preferences of the appropriate way to extend the validation capabilities. There is no need to modify the go-zero source code.

validation.Validator :

https://github.com/zeromicro/go-zero/blob/4a62d084a93fb50cb85ca62bc8620b041cdc1a4a/core/validation/validator.go#L3-L7

validator:

https://github.com/zeromicro/go-zero/blob/4a62d084a93fb50cb85ca62bc8620b041cdc1a4a/rest/httpx/requests.go#L28 https://github.com/zeromicro/go-zero/blob/4a62d084a93fb50cb85ca62bc8620b041cdc1a4a/rest/httpx/requests.go#L126-L128

I hope this answer answers your initial question.

chenquan avatar Jul 02 '24 12:07 chenquan

I got it, what about setting go-playground's validator as a default validator? I think many people would like to use it. The implementation will be lazy initialization.

Linde7777 avatar Jul 02 '24 13:07 Linde7777

Hello, I don't think it's necessary, because go-zero already provides the ability to extend validation. But you can launch a PR to allow more people to see your code to further discuss the need for a merger.

chenquan avatar Jul 02 '24 23:07 chenquan

ok, thank you!

Linde7777 avatar Jul 03 '24 00:07 Linde7777

https://medium.com/dev-genius/the-elegance-of-go-interface-b4c9c00012da

I've written an post to introduce how to use go-playground/validator in go-zero.

kevwan avatar Jan 28 '25 08:01 kevwan