🩹 Fix: Update CheckConstraint method to return true for noConstraint and improve error handling

pull/3356/head
JIeJaitt 2025-03-19 15:53:28 +08:00
parent 255ab13556
commit 778134f600
4 changed files with 60 additions and 81 deletions

View File

@ -55,14 +55,14 @@ func New(config ...Config) fiber.Handler {
// - context.Context: Retrieves request ID from context values
func FromContext(c any) string {
switch ctx := c.(type) {
case fiber.Ctx:
if rid, ok := ctx.Locals(requestIDKey).(string); ok {
return rid
}
case context.Context:
if rid, ok := ctx.Value(requestIDKey).(string); ok {
return rid
}
case fiber.Ctx:
if rid, ok := ctx.Locals(requestIDKey).(string); ok {
return rid
}
default:
log.Errorf("Unsupported context type: %T. Expected fiber.Ctx or context.Context", c)
}

107
path.go
View File

@ -674,6 +674,8 @@ func getParamConstraintType(constraintPart string) TypeConstraint {
// 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 {
// First check if there's a custom constraint with the same name
// This allows custom constraints to override built-in constraints
@ -683,6 +685,11 @@ func (c *Constraint) CheckConstraint(param string) bool {
}
}
var (
err error
num int
)
// Validate constraint has required data
needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint}
needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint}
@ -699,23 +706,16 @@ func (c *Constraint) CheckConstraint(param string) bool {
}
}
// Check constraints
// check constraints
switch c.ID {
case noConstraint:
// If we reach here with noConstraint, it means we didn't find a matching custom constraint above
return false
return true
case intConstraint:
if _, err := strconv.Atoi(param); err != nil {
return false
}
_, err = strconv.Atoi(param)
case boolConstraint:
if _, err := strconv.ParseBool(param); err != nil {
return false
}
_, err = strconv.ParseBool(param)
case floatConstraint:
if _, err := strconv.ParseFloat(param, 32); err != nil {
return false
}
_, err = strconv.ParseFloat(param, 32)
case alphaConstraint:
for _, r := range param {
if !unicode.IsLetter(r) {
@ -723,88 +723,57 @@ func (c *Constraint) CheckConstraint(param string) bool {
}
}
case guidConstraint:
if _, err := uuid.Parse(param); err != nil {
return false
}
_, err = uuid.Parse(param)
case minLenConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
data, _ := strconv.Atoi(c.Data[0])
if len(param) < data {
return false
}
case maxLenConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
data, _ := strconv.Atoi(c.Data[0])
if len(param) > data {
return false
}
case lenConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
data, _ := strconv.Atoi(c.Data[0])
if len(param) != data {
return false
}
case betweenLenConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
data2, err := strconv.Atoi(c.Data[1])
if err != nil {
return false
}
data, _ := strconv.Atoi(c.Data[0])
data2, _ := strconv.Atoi(c.Data[1])
length := len(param)
if length < data || length > data2 {
return false
}
case minConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
num, err := strconv.Atoi(param)
if err != nil {
return false
}
if num < data {
data, _ := strconv.Atoi(c.Data[0])
num, err = strconv.Atoi(param)
if err != nil || num < data {
return false
}
case maxConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
num, err := strconv.Atoi(param)
if err != nil {
return false
}
if num > data {
data, _ := strconv.Atoi(c.Data[0])
num, err = strconv.Atoi(param)
if err != nil || num > data {
return false
}
case rangeConstraint:
data, err := strconv.Atoi(c.Data[0])
if err != nil {
return false
}
data2, err := strconv.Atoi(c.Data[1])
if err != nil {
return false
}
num, err := strconv.Atoi(param)
if err != nil {
return false
}
if num < data || num > data2 {
data, _ := strconv.Atoi(c.Data[0])
data2, _ := strconv.Atoi(c.Data[1])
num, err = strconv.Atoi(param)
if err != nil || num < data || num > data2 {
return false
}
case datetimeConstraint:
if _, err := time.Parse(c.Data[0], param); err != nil {
_, err = time.Parse(c.Data[0], param)
if err != nil {
return false
}
case regexConstraint:
@ -814,7 +783,9 @@ func (c *Constraint) CheckConstraint(param string) bool {
if match := c.RegexCompiler.MatchString(param); !match {
return false
}
default:
return false
}
return true
return err == nil
}

View File

@ -280,7 +280,7 @@ func TestConstraint_CheckConstraint(t *testing.T) {
name: "no constraint",
constraint: &Constraint{ID: noConstraint},
param: "abc",
expectedResult: false,
expectedResult: true,
},
{
name: "int constraint valid",

View File

@ -28,6 +28,14 @@ var (
func init() {
// smaller list for benchmark cases
benchmarkCases = []routeCaseCollection{
{
pattern: "/api/v1/:param<int;bool((>",
testCases: []routeTestCase{
{url: "/api/v1/entity", params: nil, match: false},
{url: "/api/v1/8728382", params: []string{"8728382"}, match: true},
{url: "/api/v1/true", params: nil, match: false},
},
},
{
pattern: "/api/v1/const",
testCases: []routeTestCase{
@ -606,14 +614,14 @@ func init() {
{url: "/api/v1/2022/08-27", params: nil, match: false},
},
},
{
pattern: "/api/v1/:param<int;bool((>",
testCases: []routeTestCase{
{url: "/api/v1/entity", params: nil, match: false},
{url: "/api/v1/8728382", params: []string{"8728382"}, match: true},
{url: "/api/v1/true", params: nil, match: false},
},
},
// {
// pattern: "/api/v1/:param<int;bool((>",
// testCases: []routeTestCase{
// {url: "/api/v1/entity", params: nil, match: false},
// {url: "/api/v1/8728382", params: []string{"8728382"}, match: true},
// {url: "/api/v1/true", params: nil, match: false},
// },
// },
{
pattern: "/api/v1/:param<int;max(3000)>",
testCases: []routeTestCase{