Skip to content

Commit 104bea0

Browse files
committed
Add new x/errors/validation package to make your life even more easier (using Generics)
1 parent 8f2deb6 commit 104bea0

File tree

14 files changed

+443
-282
lines changed

14 files changed

+443
-282
lines changed

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
2323

2424
Changes apply to `main` branch.
2525

26+
- Simplify how validation errors on `/x/errors` package works. A new `x/errors/validation` sub-package added to make your life easier (using the powerful Generics feature).
2627
- Add `x/errors.OK`, `Create`, `NoContent` and `NoContentOrNotModified` package-level generic functions as custom service method caller helpers. Example can be found [here](_examples/routing/http-wire-errors/service/main.go).
2728
- Add `x/errors.ReadPayload`, `ReadQuery`, `ReadPaginationOptions`, `Handle`, `HandleCreate`, `HandleCreateResponse`, `HandleUpdate` and `HandleDelete` package-level functions as helpers for common actions.
2829
- Add `x/jsonx.GetSimpleDateRange(date, jsonx.WeekRange, time.Monday, time.Sunday)` which returns all dates between the given range and start/end weekday values for WeekRange.

_examples/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@
5454
* [Basic](routing/basic/main.go)
5555
* [Custom HTTP Errors](routing/http-errors/main.go)
5656
* [HTTP Wire Errors](routing/http-wire-errors/main.go) **NEW**
57-
* [Custom Validation Errors](routing/http-wire-errors/custom-validation-errors/main.go)
58-
* [Service](routing/http-wire-errors/service/main.go) **NEW**
57+
* [Service and Validation](routing/http-wire-errors/service/main.go) **NEW**
5958
* [Not Found - Intelligence](routing/intelligence/main.go)
6059
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
6160
* [Dynamic Path](routing/dynamic-path/main.go)

_examples/README_ZH_HANT.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
- [Basic](routing/basic/main.go)
5252
- [Custom HTTP Errors](routing/http-errors/main.go)
5353
- [HTTP Wire Errors](routing/http-wire-errors/main.go) **新範例**
54-
- [Custom Validation Errors](routing/http-wire-errors/custom-validation-errors/main.go)
5554
- [Not Found - Intelligence](routing/intelligence/main.go)
5655
- [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
5756
- [Dynamic Path](routing/dynamic-path/main.go)

_examples/routing/http-wire-errors/custom-validation-errors/main.go

Lines changed: 0 additions & 119 deletions
This file was deleted.

_examples/routing/http-wire-errors/service/main.go

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import (
66

77
"github.com/kataras/iris/v12"
88
"github.com/kataras/iris/v12/x/errors"
9+
"github.com/kataras/iris/v12/x/errors/validation"
910
)
1011

1112
func main() {
1213
app := iris.New()
13-
1414
service := new(myService)
15+
1516
app.Post("/", createHandler(service))
1617
app.Get("/", listHandler(service))
1718
app.Delete("/{id:string}", deleteHandler(service))
@@ -70,16 +71,65 @@ type (
7071
myService struct{}
7172

7273
CreateRequest struct {
73-
Fullname string
74+
Fullname string `json:"fullname"`
75+
Age int `json:"age"`
76+
Hobbies []string `json:"hobbies"`
7477
}
7578

7679
CreateResponse struct {
77-
ID string
78-
Firstname string
79-
Lastname string
80+
ID string `json:"id"`
81+
Firstname string `json:"firstname"`
82+
Lastname string `json:"lastname"`
83+
Age int `json:"age"`
84+
Hobbies []string `json:"hobbies"`
8085
}
8186
)
8287

88+
// ValidateContext implements the errors.ContextValidator interface.
89+
// It validates the request body and returns an error if the request body is invalid.
90+
// You can also alter the "r" CreateRequest before calling the service method,
91+
// e.g. give a default value to a field if it's empty or set an ID based on a path parameter.
92+
func (r *CreateRequest) ValidateContext(ctx iris.Context) error {
93+
// To pass custom validation functions:
94+
// return validation.Join(
95+
// validation.String("fullname", r.Fullname).Func(customStringFuncHere),
96+
// OR
97+
// validation.Field("any_field", r.AnyFieldValue).Func(customAnyFuncHere))
98+
return validation.Join(
99+
validation.String("fullname", r.Fullname).NotEmpty().Fullname().Length(3, 50),
100+
validation.Number("age", r.Age).InRange(18, 130),
101+
validation.Slice("hobbies", r.Hobbies).Length(1, 10),
102+
)
103+
104+
/* Example Output:
105+
{
106+
"http_error_code": {
107+
"canonical_name": "INVALID_ARGUMENT",
108+
"status": 400
109+
},
110+
"message": "validation failure",
111+
"details": "fields were invalid",
112+
"validation": [
113+
{
114+
"field": "fullname",
115+
"value": "",
116+
"reason": "must not be empty, must contain first and last name, must be between 3 and 50 characters"
117+
},
118+
{
119+
"field": "age",
120+
"value": 0,
121+
"reason": "must be in range of [18, 130]"
122+
},
123+
{
124+
"field": "hobbies",
125+
"value": null,
126+
"reason": "must be between 1 and 10 elements"
127+
}
128+
]
129+
}
130+
*/
131+
}
132+
83133
func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateResponse, error) {
84134
arr := strings.Split(in.Fullname, " ")
85135
firstname, lastname := arr[0], arr[1]
@@ -89,6 +139,8 @@ func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateRespons
89139
ID: id,
90140
Firstname: firstname,
91141
Lastname: lastname,
142+
Age: in.Age,
143+
Hobbies: in.Hobbies,
92144
}
93145
return resp, nil // , errors.New("create: test error")
94146
}

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ require (
3232
github.com/mailgun/raymond/v2 v2.0.48
3333
github.com/mailru/easyjson v0.7.7
3434
github.com/microcosm-cc/bluemonday v1.0.26
35-
github.com/redis/go-redis/v9 v9.3.1
35+
github.com/redis/go-redis/v9 v9.4.0
3636
github.com/schollz/closestmatch v2.1.0+incompatible
3737
github.com/shirou/gopsutil/v3 v3.23.12
38-
github.com/tdewolff/minify/v2 v2.20.10
38+
github.com/tdewolff/minify/v2 v2.20.12
3939
github.com/vmihailenco/msgpack/v5 v5.4.1
4040
github.com/yosssi/ace v0.0.5
4141
go.etcd.io/bbolt v1.3.8
4242
golang.org/x/crypto v0.17.0
43+
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
4344
golang.org/x/net v0.19.0
44-
golang.org/x/sys v0.15.0
45+
golang.org/x/sys v0.16.0
4546
golang.org/x/text v0.14.0
4647
golang.org/x/time v0.5.0
4748
google.golang.org/protobuf v1.32.0
@@ -107,7 +108,6 @@ require (
107108
github.com/yudai/gojsondiff v1.0.0 // indirect
108109
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
109110
github.com/yusufpapurcu/wmi v1.2.3 // indirect
110-
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
111111
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
112112
moul.io/http2curl/v2 v2.3.0 // indirect
113113
)

go.sum

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x/errors/errors.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,21 @@ func HandleError(ctx *context.Context, err error) bool {
174174
return true
175175
}
176176

177-
if vErrs, ok := AsValidationErrors(err); ok {
178-
InvalidArgument.Data(ctx, "validation failure", vErrs)
177+
if vErr, ok := err.(ValidationError); ok {
178+
if vErr == nil {
179+
return false // consider as not error for any case, this should never happen.
180+
}
181+
182+
InvalidArgument.Validation(ctx, vErr)
183+
return true
184+
}
185+
186+
if vErrs, ok := err.(ValidationErrors); ok {
187+
if len(vErrs) == 0 {
188+
return false // consider as not error for any case, this should never happen.
189+
}
190+
191+
InvalidArgument.Validation(ctx, vErrs...)
179192
return true
180193
}
181194

@@ -283,9 +296,20 @@ func (e ErrorCodeName) Err(ctx *context.Context, err error) {
283296
return
284297
}
285298

286-
if validationErrors, ok := AsValidationErrors(err); ok {
287-
e.validation(ctx, validationErrors)
288-
return
299+
if vErr, ok := err.(ValidationError); ok {
300+
if vErr == nil {
301+
return // consider as not error for any case, this should never happen.
302+
}
303+
304+
e.Validation(ctx, vErr)
305+
}
306+
307+
if vErrs, ok := err.(ValidationErrors); ok {
308+
if len(vErrs) == 0 {
309+
return // consider as not error for any case, this should never happen.
310+
}
311+
312+
e.Validation(ctx, vErrs...)
289313
}
290314

291315
// If it's already an Error type then send it directly.

0 commit comments

Comments
 (0)