Skip to content

Commit 6938bf3

Browse files
committed
chore: update binding and validation docs
1 parent f63ff55 commit 6938bf3

File tree

2 files changed

+313
-53
lines changed

2 files changed

+313
-53
lines changed

content/en/docs/hertz/tutorials/basic-feature/binding-and-validate.md

Lines changed: 156 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Binding and validate"
3-
date: 2022-06-21
3+
date: 2025-12-08
44
weight: 8
55
keywords:
66
["Binding and validate", "go-tagexpr", "tag", "Parameter binding precedence"]
@@ -129,7 +129,7 @@ type TagRequiredReq struct {
129129
130130
### Customise binder
131131

132-
> hertz version >= v0.7.0 support
132+
> hertz version >= v0.10.3 support
133133
134134
You need to implement the Binder interface and inject it into the hertz engine in a configurable way.
135135

@@ -138,13 +138,13 @@ type Binder interface {
138138
Name() string // The name of the binder.
139139
// The following are the various binding methods
140140
Bind(*protocol.Request, interface{}, param.Params) error
141-
BindAndValidate(*protocol.Request, interface{}, param.Params) error
142141
BindQuery(*protocol.Request, interface{}) error
143142
BindHeader(*protocol.Request, interface{}) error
144143
BindPath(*protocol.Request, interface{}, param.Params) error
145144
BindForm(*protocol.Request, interface{}) error
146145
BindJSON(*protocol.Request, interface{}) error
147146
BindProtobuf(*protocol.Request, interface{}) error
147+
Validate(*protocol.Request, interface{}) error
148148
}
149149
```
150150

@@ -170,10 +170,6 @@ func (m *mockBinder) Bind(request *protocol.Request, i interface{}, params param
170170
return nil
171171
}
172172

173-
func (m *mockBinder) BindAndValidate(request *protocol.Request, i interface{}, params param.Params) error {
174-
return fmt.Errorf("test binder")
175-
}
176-
177173
func (m *mockBinder) BindQuery(request *protocol.Request, i interface{}) error {
178174
return nil
179175
}
@@ -198,15 +194,42 @@ func (m *mockBinder) BindProtobuf(request *protocol.Request, i interface{}) erro
198194
return nil
199195
}
200196

197+
func (m *mockBinder) Validate(request *protocol.Request, i interface{}) error {
198+
return nil
199+
}
200+
201201
```
202202

203203
Currently expanded binders:
204204

205+
> ⚠️ Note: The `hertz-contrib/binding` middleware is now deprecated.
206+
> Users are recommended to use built-in functionality in Hertz or their own custom binder.
207+
205208
- bytedance/go-tagexpr: https://github.com/hertz-contrib/binding/tree/main/go_tagexpr (binding library used before refactoring)
206209

207210
### Custom validator
208211

209-
> Supported by hertz version >= v0.7.0.
212+
> Supported by hertz version >= v0.10.3.
213+
214+
```go
215+
import (
216+
"github.com/go-playground/validator/v10"
217+
)
218+
219+
func main() {
220+
vd := validator.New(validator.WithRequiredStructEnabled())
221+
h := server.Default(server.WithHostPorts("127.0.0.1:8080"),
222+
server.WithCustomValidatorFunc(func(_ *protocol.Request, req any) error {
223+
return vd.Struct(req)
224+
}),
225+
)
226+
h.Spin()
227+
}
228+
```
229+
230+
#### Custom validator (Deprecated)
231+
232+
> Supported by Hertz versions 0.7.0 to 0.10.2.
210233
211234
You need to implement the Validator interface and inject it into the hertz engine in a configurable way.
212235

@@ -247,13 +270,16 @@ func (m *mockValidator) ValidateTag() string {
247270

248271
Currently expanded validators:
249272

273+
> ⚠️ Note: The `hertz-contrib/binding` middleware is now deprecated.
274+
> Users are recommended to use the built-in functionality in Hertz. If custom validation is required, users can use the validator from [go-playground/validator](https://github.com/go-playground/validator).
275+
250276
- go-playground/validator: https://github.com/hertz-contrib/binding/tree/main/go_playground
251277

252278
### Customize the error of binding and validation
253279

254280
When an error occurs in the binding parameter and the parameter validation fails, user can customize the Error([demo](https://github.com/cloudwego/hertz-examples/tree/main/binding/custom_error))For example:
255281
The user can customise the content of the Error in case of binding parameter errors and parameter validation failures, using the following method:<br>
256-
**hertz version >= v0.7.0**
282+
**hertz version >= v0.10.3**
257283

258284
> Custom bind errors are not supported at this time.
259285
@@ -262,6 +288,77 @@ Custom validate error:
262288
```go
263289
package main
264290

291+
import (
292+
"context"
293+
"fmt"
294+
295+
"github.com/cloudwego/hertz/pkg/app"
296+
"github.com/cloudwego/hertz/pkg/app/server"
297+
"github.com/cloudwego/hertz/pkg/protocol"
298+
"github.com/go-playground/validator/v10"
299+
)
300+
301+
type User struct {
302+
Name string `form:"name" validate:"required"`
303+
Age uint8 `form:"age" validate:"gte=0,lte=130"`
304+
Email string `form:"email" validate:"required,email"`
305+
}
306+
307+
type ValidateError struct {
308+
ErrType, FailField, Msg string
309+
}
310+
311+
func (e *ValidateError) Error() string {
312+
if e.Msg != "" {
313+
return e.ErrType + ": expr_path=" + e.FailField + ", cause=" + e.Msg
314+
}
315+
return e.ErrType + ": expr_path=" + e.FailField + ", cause=invalid"
316+
}
317+
318+
func main() {
319+
v := validator.New(validator.WithRequiredStructEnabled())
320+
321+
h := server.Default(
322+
server.WithHostPorts("127.0.0.1:8080"),
323+
server.WithCustomValidatorFunc(func(_ *protocol.Request, req any) error {
324+
err := v.Struct(req)
325+
if err == nil {
326+
return nil
327+
}
328+
329+
if ve, ok := err.(validator.ValidationErrors); ok {
330+
fe := ve[0]
331+
332+
return &ValidateError{
333+
ErrType: "validateErr",
334+
FailField: fe.Field(),
335+
Msg: fe.Tag(),
336+
}
337+
}
338+
339+
return err
340+
}),
341+
)
342+
343+
h.GET("/bind", func(ctx context.Context, c *app.RequestContext) {
344+
var user User
345+
err := c.BindAndValidate(&user)
346+
if err != nil {
347+
fmt.Println("CUSTOM:", err.Error())
348+
return
349+
}
350+
fmt.Println("OK:", user)
351+
})
352+
353+
h.Spin()
354+
}
355+
```
356+
357+
**hertz versions 0.7.0 to 0.10.2**<br>
358+
359+
```go
360+
package main
361+
265362
import (
266363
"github.com/cloudwego/hertz/pkg/app/server/binding"
267364
"github.com/cloudwego/hertz/pkg/app/server"
@@ -356,34 +453,6 @@ func init() {
356453
In the parameter binding, for some special types, when the default behavior can not meet the demand, you can use the custom type resolution to solve the problem, the use of the following: <br>
357454
**hertz version >= v0.7.0**<br>
358455

359-
```go
360-
import "github.com/cloudwego/hertz/pkg/app/server/binding"
361-
362-
type Nested struct {
363-
B string
364-
C string
365-
}
366-
367-
type TestBind struct {
368-
A Nested `query:"a,required"`
369-
}
370-
371-
func init() {
372-
binding.MustRegTypeUnmarshal(reflect.TypeOf(Nested{}), func(v string, emptyAsZero bool) (reflect.Value, error) {
373-
if v == "" && emptyAsZero {
374-
return reflect.ValueOf(Nested{}), nil
375-
}
376-
val := Nested{
377-
B: v[:5],
378-
C: v[5:],
379-
}
380-
return reflect.ValueOf(val), nil
381-
})
382-
}
383-
```
384-
385-
### Customize the validation function
386-
387456
```go
388457
package main
389458

@@ -402,7 +471,7 @@ type TestBind struct {
402471
}
403472

404473
func main() {
405-
bindConfig := &binding.BindConfig{}
474+
bindConfig := binding.NewBindConfig()
406475
// After v0.7.0 refactoring, on the basis of the original increase in the request content and routing parameters,
407476
// which can be more flexible for the user to customise the type of parsing
408477
// Note: Only after a tag is successfully matched will the custom logic go through.
@@ -456,8 +525,55 @@ func init() {
456525

457526
### Custom validation function
458527

459-
Complex validation logic can be implemented in the `vd` annotation by registering a custom validation function:<br>
460-
**hertz version >= v0.7.0**<br>
528+
Complex validation logic can be implemented in the `validate` annotation by registering a custom validation function:<br>
529+
**hertz version >= v0.10.3**<br>
530+
531+
```go
532+
package main
533+
534+
import (
535+
"context"
536+
"fmt"
537+
538+
"github.com/cloudwego/hertz/pkg/app"
539+
"github.com/cloudwego/hertz/pkg/app/server"
540+
"github.com/cloudwego/hertz/pkg/app/server/binding"
541+
"github.com/cloudwego/hertz/pkg/protocol"
542+
"github.com/go-playground/validator/v10"
543+
)
544+
545+
type Req struct {
546+
A string `query:"a" validate:"test"`
547+
}
548+
549+
func main() {
550+
vd := validator.New(validator.WithRequiredStructEnabled())
551+
552+
vd.RegisterValidation("test", func(fl validator.FieldLevel) bool {
553+
return fl.Field().String() != "123"
554+
})
555+
556+
h := server.Default(
557+
server.WithHostPorts("127.0.0.1:8080"),
558+
server.WithCustomValidatorFunc(func(_ *protocol.Request, req any) error {
559+
return vd.Struct(req)
560+
}),
561+
)
562+
563+
h.GET("/test", func(ctx context.Context, c *app.RequestContext) {
564+
var r Req
565+
if err := c.BindAndValidate(&r); err != nil {
566+
fmt.Println("VALIDATION ERROR:", err.Error())
567+
return
568+
}
569+
fmt.Println("OK:", r)
570+
})
571+
572+
h.Spin()
573+
}
574+
```
575+
576+
**hertz versions 0.7.0 to 0.10.2**<br>
461577

462578
```go
463579
package main

0 commit comments

Comments
 (0)