mirror of https://github.com/gofiber/fiber.git
🔥 Feature: Enhance CheckConstraint method for improved error handling (#3356)
* 🔥 Feature: Enhance CheckConstraint method for improved error handling * Revert "🔥 Feature: Enhance CheckConstraint method for improved error handling" This reverts commitdocs-emoji68e8777b4c
. * Reapply "🔥 Feature: Enhance CheckConstraint method for improved error handling" This reverts commit9e6c8e68df
. * 🚨 Test: Add comprehensive tests for CheckConstraint method with various constraint scenarios * 🩹 Fix: lint error * 🩹 Fix: Update CheckConstraint method to return true for noConstraint and improve error handling * ♻️ Refactor: Remove unused CheckConstraint test cases and reorganize benchmark test cases for clarity * ♻️ Refactor: Remove outdated test cases from path_testcases_test.go and clean up CheckConstraint method in path.go * 📚 Doc: Update custom constraints section to clarify overriding behavior * 🔥 Feature: Enhance CheckConstraint method for improved error handling * Revert "🔥 Feature: Enhance CheckConstraint method for improved error handling" This reverts commit68e8777b4c
. * Reapply "🔥 Feature: Enhance CheckConstraint method for improved error handling" This reverts commit9e6c8e68df
. * 🚨 Test: Add comprehensive tests for CheckConstraint method with various constraint scenarios * 🩹 Fix: lint error * 🩹 Fix: Update CheckConstraint method to return true for noConstraint and improve error handling * ♻️ Refactor: Remove unused CheckConstraint test cases and reorganize benchmark test cases for clarity * ♻️ Refactor: Remove outdated test cases from path_testcases_test.go and clean up CheckConstraint method in path.go * 📚 Doc: Update custom constraints section to clarify overriding behavior * 📚 Doc: Add caution note about custom constraints overriding built-in constraints in routing guide --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: RW <rene@gofiber.io>
parent
bb12633c8b
commit
c5c7f86d85
|
@ -250,6 +250,10 @@ app.Get("/:test<int>?", func(c fiber.Ctx) error {
|
||||||
|
|
||||||
Custom constraints can be added to Fiber using the `app.RegisterCustomConstraint` method. Your constraints have to be compatible with the `CustomConstraint` interface.
|
Custom constraints can be added to Fiber using the `app.RegisterCustomConstraint` method. Your constraints have to be compatible with the `CustomConstraint` interface.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
Attention, custom constraints can now override built-in constraints. If a custom constraint has the same name as a built-in constraint, the custom constraint will be used instead. This allows for more flexibility in defining route parameter constraints.
|
||||||
|
:::
|
||||||
|
|
||||||
It is a good idea to add external constraints to your project once you want to add more specific rules to your routes.
|
It is a good idea to add external constraints to your project once you want to add more specific rules to your routes.
|
||||||
For example, you can add a constraint to check if a parameter is a valid ULID.
|
For example, you can add a constraint to check if a parameter is a valid ULID.
|
||||||
|
|
||||||
|
|
39
path.go
39
path.go
|
@ -672,12 +672,25 @@ func getParamConstraintType(constraintPart string) TypeConstraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:errcheck // TODO: Properly check _all_ errors in here, log them & immediately return
|
// CheckConstraint validates if a param matches the given constraint
|
||||||
|
// Returns true if the param passes the constraint check, false otherwise
|
||||||
|
//
|
||||||
|
//nolint:errcheck // TODO: Properly check _all_ errors in here, log them or immediately return
|
||||||
func (c *Constraint) CheckConstraint(param string) bool {
|
func (c *Constraint) CheckConstraint(param string) bool {
|
||||||
var err error
|
// First check if there's a custom constraint with the same name
|
||||||
var num int
|
// This allows custom constraints to override built-in constraints
|
||||||
|
for _, cc := range c.customConstraints {
|
||||||
|
if cc.Name() == c.Name {
|
||||||
|
return cc.Execute(param, c.Data...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check data exists
|
var (
|
||||||
|
err error
|
||||||
|
num int
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate constraint has required data
|
||||||
needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint}
|
needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint}
|
||||||
needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint}
|
needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint}
|
||||||
|
|
||||||
|
@ -696,11 +709,7 @@ func (c *Constraint) CheckConstraint(param string) bool {
|
||||||
// check constraints
|
// check constraints
|
||||||
switch c.ID {
|
switch c.ID {
|
||||||
case noConstraint:
|
case noConstraint:
|
||||||
for _, cc := range c.customConstraints {
|
return true
|
||||||
if cc.Name() == c.Name {
|
|
||||||
return cc.Execute(param, c.Data...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case intConstraint:
|
case intConstraint:
|
||||||
_, err = strconv.Atoi(param)
|
_, err = strconv.Atoi(param)
|
||||||
case boolConstraint:
|
case boolConstraint:
|
||||||
|
@ -744,14 +753,14 @@ func (c *Constraint) CheckConstraint(param string) bool {
|
||||||
data, _ := strconv.Atoi(c.Data[0])
|
data, _ := strconv.Atoi(c.Data[0])
|
||||||
num, err = strconv.Atoi(param)
|
num, err = strconv.Atoi(param)
|
||||||
|
|
||||||
if num < data {
|
if err != nil || num < data {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case maxConstraint:
|
case maxConstraint:
|
||||||
data, _ := strconv.Atoi(c.Data[0])
|
data, _ := strconv.Atoi(c.Data[0])
|
||||||
num, err = strconv.Atoi(param)
|
num, err = strconv.Atoi(param)
|
||||||
|
|
||||||
if num > data {
|
if err != nil || num > data {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case rangeConstraint:
|
case rangeConstraint:
|
||||||
|
@ -759,12 +768,18 @@ func (c *Constraint) CheckConstraint(param string) bool {
|
||||||
data2, _ := strconv.Atoi(c.Data[1])
|
data2, _ := strconv.Atoi(c.Data[1])
|
||||||
num, err = strconv.Atoi(param)
|
num, err = strconv.Atoi(param)
|
||||||
|
|
||||||
if num < data || num > data2 {
|
if err != nil || num < data || num > data2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case datetimeConstraint:
|
case datetimeConstraint:
|
||||||
_, err = time.Parse(c.Data[0], param)
|
_, err = time.Parse(c.Data[0], param)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case regexConstraint:
|
case regexConstraint:
|
||||||
|
if c.RegexCompiler == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if match := c.RegexCompiler.MatchString(param); !match {
|
if match := c.RegexCompiler.MatchString(param); !match {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -713,6 +713,14 @@ func init() {
|
||||||
{url: "/api/v1/", params: []string{""}, match: true},
|
{url: "/api/v1/", params: []string{""}, match: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Add test case for RegexCompiler == nil
|
||||||
|
{
|
||||||
|
pattern: "/api/v1/:param<regex(\\d+)>",
|
||||||
|
testCases: []routeTestCase{
|
||||||
|
{url: "/api/v1/123", params: []string{"123"}, match: true},
|
||||||
|
{url: "/api/v1/abc", params: nil, match: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
}...,
|
}...,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue