fiber
fiber copied to clipboard
π Request: Route Constraints
Route constraints execute when a match has occurred to the incoming URL and the URL path is tokenized into route values. I found this very useful when defining routes. ASP NET Core was inspiration for this feature.
Example
app.Get("/users/{id:int}", func (ctx *fiber.Ctx) {
// will come here if id is int
// otherwise return 404
})
Proposed constraints:
Constraint | Example | Example matches |
---|---|---|
int | {id:int} | 123456789, -123456789 |
bool | {active:bool} | true,false |
datetime | {dob:datetime} | 2016-12-31,Β 2016-12-31 7:32pm |
guid | {id:guid} | CD2C1638-1638-72D5-1638-DEADBEEF1638 |
float | {weight:float} | 1.234,Β -1,001.01e8 |
minlength(value) | {username:minlength(4)} | Test (must be at least 4 characters) |
maxlength(value) | {filename:maxlength(8)} | MyFile (must be no more than 8 characters |
length(length) | {filename:length(12)} | somefile.txt (exactly 12 characters) |
min(value) | {age:min(18)} | 19 (Integer value must be at least 18) |
max(value) | {age:max(120)} | 91 (Integer value must be no more than 120) |
range(min,max) | {age:range(18,120)} | 91 (Integer value must be at least 18 but no more than 120) |
alpha | {name:alpha} | Rick (String must consist of one or more alphabetical characters,Β a-zΒ and case-insensitive) |
regex(expression) | {ssn:regex(^\d{{3}}-\d{{2}}-\d{{4}}$)} | 123-45-6789 (Must match regular expression) |
More details: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0#route-constraints
Thanks for opening your first issue here! π Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord
hello @CerealKiller97, are you working on this feature? if not, can you or @efectn assign it to me, I would love to work on it
hello @CerealKiller97, are you working on this feature? if not, can you or @efectn assign it to me, I would love to work on it
Assigned. If @CerealKiller97 isn't working on this, you can start working ππ
It is looking good. This feature can open from fiber.Config for v2. Example:
fiber.New(fiber.Config{
EnableRouteConstraints: true,
})
What do you think about it ?
It is looking good. This feature can open from fiber.Config for v2. Example:
fiber.New(fiber.Config{ EnableRouteConstraints: true, })
What do you think about it ?
I think something like this can be done too instead of adding extra config var:
app.Get("/users/:id:int", func (ctx *fiber.Ctx) {
// will come here if id is int
// otherwise return 404
})
I think something like this can be done too instead of adding extra config var:
I think it is not understandable.
My thought was to do it also without config switches and really use the same syntax as in the table, as alternative
Important is the speed, there should be hardly any time added when analyzing the route (so string based detection, except for the part with regexes)
hello @CerealKiller97, are you working on this feature? if not, can you or @efectn assign it to me, I would love to work on it
Hello @MohabMohamed you can work on this. Sorry for waiting for my reply. π
Hello @MohabMohamed you can work on this. Sorry for waiting for my reply.
Thank you @CerealKiller97
I think something like this can be done too instead of adding extra config var: I think we can do something like this to differentiate between the param name and its type, and without changing unconstrained params:
app.Get("/users/:id<int>", func (ctx *fiber.Ctx) {
// will come here if id is int
// otherwise return 404
})
app.Get("/posts/:id", func (ctx *fiber.Ctx) {
})
what do you think?
would also be possible
but it would be good if we use patterns that are already used in other frameworks/languages
maybe someone can do some research
the goal is to provide people with familiar structures that they have already been exposed to in order to promote acceptance and knowledge sharing
Hello @MohabMohamed you can work on this. Sorry for waiting for my reply.
Thank you @CerealKiller97
I think something like this can be done too instead of adding extra config var: I think we can do something like this to differentiate between the param name and its type, and without changing unconstrained params:
app.Get("/users/:id<int>", func (ctx *fiber.Ctx) { // will come here if id is int // otherwise return 404 }) app.Get("/posts/:id", func (ctx *fiber.Ctx) { })
what do you think?
I think this is the best way because of fiber router doesn't have adding parameters by {}
I think this is the best way because of fiber router doesn't have adding parameters by {}
well, that would not be too complex to provide this possibility as an alternative syntax
because this "{" is a fixed character you can use it as a delimiter and mark it in the segments of the routes
which has to happen anyway for the constrains
I think this is the best way because of fiber router doesn't have adding parameters by {}
well, that would not be too complex to provide this possibility as an alternative syntax
because this "{" is a fixed character you can use it as a delimiter and mark it in the segments of the routes
which has to happen anyway for the contrains
Yes but Fiber is Express inspired framework. Wouldn't adding {}, make Fiber more complex?
I think if we want to add it with { } we could do this in V3 and change the params without constraints to follow this pattern too, but we can't do this with the already in use version,and I think the constrained parameters should be derived from the regular params
and the only constrained params shape I know is flask it like this:
/users/<int:id>
maybe but since express does not offer this feature we have to choose a structure
would ask for research a small list of frameworks/languages that offer this feature and their structure, i think this can only be beneficial and we might find some more additional constrains
I think if we want to add it with { } we could do this in V3 and change the params without constraints to follow this pattern too, but we can't do this with the already in use version,and I think the constrained parameters should be derived from the regular params
think we can do this in version 2 as well since we still support the old notation we don't change anything just add functionality
https://laravel.com/docs/9.x/routing#parameters-regular-expression-constraints
https://www.c-sharpcorner.com/blogs/asp-net-core-route-constraints
... maybe some more research
we could also leave this declaration completely out of the url and provide a method in the context which receives an object with the constrains and validates it
// instead of "/users/:id<int>"
app.Get("/users/:id", func (ctx *fiber.Ctx) {
err := ctx.CheckRouteParams(fiber.Map{
id: "int",
})
})
// instead of "/users/:id<int>"
app.Get("/users/:id", func (ctx *fiber.Ctx) {
err := ctx.RouteConstrains().whereNumber("id")(....more...checks....).check();
})
we could also leave this declaration completely out of the url and provide a method in the context which receives an object with the constrains and validates it
// instead of "/users/:id<int>" app.Get("/users/:id", func (ctx *fiber.Ctx) { err := ctx.CheckRouteParams(fiber.Map{ id: "int", }) })
// instead of "/users/:id<int>" app.Get("/users/:id", func (ctx *fiber.Ctx) { err := ctx.RouteConstrains().whereNumber("id")(....more...checks....).check(); })
We can also do it by using latestRoute var of App struct -similar to usage of Name()-
app.Get("/users/:id", func (ctx *fiber.Ctx) {
...
}).WhereNumber("id")
However, id<int>
way sounds better.
I don't prefer chaining as it will make sometimes a long chain of function calls in the client code. this sounds good :
// instead of "/users/:id<int>" app.Get("/users/:id", func (ctx *fiber.Ctx) { err := ctx.CheckRouteParams(fiber.Map{ id: "int", }) })
and also this:
However,
id<int>
way sounds better.
so what's your final decision? so I can start working on this.
The route syntax is okay for me.
Just know that later there will be questions or feature requests if the error messages we generate due to a mismatch are customizable.
So we should create formatted strings for this, which are customizable from outside (please not via config -> already quite a lot of settings)
Or ask if we can customize the structure (json, xml, html) and the status code
If a mismatch is detected, we should pass the error message which is composed of the type and the parameter name as a string into a generator function, which then generates the response.
so that we can make it overloadable and give the user all possibilities
I think we should just return 404 as not matching the type of the param should be the same behavior as not matching any route.
and if the user wants to create a custom response when matching, he could use :id
notation after the specific type one. so fiber while skipping the constrained routes and then match the generic one and the user can do what ever he wants in this route.
or do you have another idea?
yes would go, but would not be quite optimal
users will think that there is already a detection of which parameters do not match and expect this information to be returned to the consumer of the api with the exact instructions about the error
don't think users will be happy if a route constrains feature exists and because they want to customize the response in case it doesn't match, they have to rebuild the entire detection feature and generate their own response
we should provide in the case the possibility to customize the response
the c# feature also has a custom error message with the parameters that do not match
if a mismatch happened we can include this data in the ctx
and we can implement a standard middleware to return these errors and run it after the route. so the user can use it if he wants or implement his own. I think that would be a clean solution.
okay gladly, as long as this does not produce too many allocations, if we keep this data available that is acceptable
performance and simplicity is fiber's recipe for success -> don't want to change anything and continue this idea
Does adding too much option makes fiber more complex in terms of developer's perspective? or compilation time? Silly question, why can't we just use regex?
In gorilla/mux we do it this way /users/{id:[0-9]+}
it means we want to disregard /users/whatever
in the routing and only accept int/numeric value.
This is standard in laravel/expressjs too, this is even notable part of expressjs where there are insane regex registration that we can apply, such as:
// from: https://expressjs.com/en/guide/routing.html
/user/:userId(\d+)
If performance is really the problem then maybe adding that fiber config should disregard regex checking