Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Routes method to support getting all registered routes #995

Closed
wants to merge 5 commits into from

Conversation

devhaozi
Copy link
Member

@devhaozi devhaozi commented Apr 7, 2025

📑 Description

Closes goravel/goravel#632

✅ Checks

  • Added test cases for my code

@Copilot Copilot bot review requested due to automatic review settings April 7, 2025 09:46
@devhaozi devhaozi requested a review from a team as a code owner April 7, 2025 09:46
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

Copy link

codecov bot commented Apr 7, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.04%. Comparing base (9e85faf) to head (ebbc8fe).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #995   +/-   ##
=======================================
  Coverage   70.04%   70.04%           
=======================================
  Files         168      168           
  Lines       11353    11355    +2     
=======================================
+ Hits         7952     7954    +2     
  Misses       3050     3050           
  Partials      351      351           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.


// Info represents a request route's specification which contains method and path and its handler.
type Info struct {
Method string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about defining some constants for this field? User may not know what this is: GET or get?

Copy link
Member Author

@devhaozi devhaozi Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about defining some constants for this field? User may not know what this is: GET or get?

image

Looks like can't use these fields directly, since they are defined as string, not a custom type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, if so, string should be fine. Could you add a comment to this field to tell the user what this field will be like?

Copy link
Contributor

@almas1992 almas1992 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the Route interface include a Routes() []Info method to expose route information?

Recover(recoverFunc func(ctx contractshttp.Context, err any))
// Routes returns all registered routes.
Routes() []Info
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be facades.Route().Routes, a bit strange. How about List or something else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be facades.Route().Routes, a bit strange. How about List or something else?

image

How about GetRoutes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be facades.Route().Routes, a bit strange. How about List or something else?

image

And gin use Routes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Em, if so, I suggest List. Any thoughts, please? cc @almas1992

Copy link
Contributor

@almas1992 almas1992 Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree facades.Route().List() seems more appropriate.

@almas1992
Copy link
Contributor

almas1992 commented Apr 8, 2025

I feel like there’s something off with the current implementation of Routes() []route.Info.

The returned handler names are mostly like github.com/goravel/gin.(*Group).xxx.handlerToGinHandler.func1, which aren’t meaningful or helpful.

For example, when I register a route like:

userController := controllers.NewUserController()
facades.Route().Get("/users/{id}", userController.Show)

The current Routes() output gives me a handler name like github.com/goravel/gin.handlerToGinHandler.func1, which doesn’t reflect the actual method being used.

Ideally, it should return something more meaningful, like goravel/app/http/controllers.(*UserController), which is what dlv (Delve debugger) displays. That format is much more informative and would be very useful for tools like artisan route:list to display readable and traceable route-handler mappings.

image

@devhaozi
Copy link
Member Author

devhaozi commented Apr 8, 2025

I feel like there’s something off with the current implementation of Routes() []route.Info.

The returned handler names are mostly like github.com/goravel/gin.(*Group).xxx.handlerToGinHandler.func1, which aren’t meaningful or helpful.

For example, when I register a route like:

userController := controllers.NewUserController()
facades.Route().Get("/users/{id}", userController.Show)

The current Routes() output gives me a handler name like github.com/goravel/gin.handlerToGinHandler.func1, which doesn’t reflect the actual method being used.

Ideally, it should return something more meaningful, like goravel/app/http/controllers.(*UserController), which is what dlv (Delve debugger) displays. That format is much more informative and would be very useful for tools like artisan route:list to display readable and traceable route-handler mappings.

image

Seems hard to do this, any suggestions? Or maybe just remove the Handler field?

@almas1992
Copy link
Contributor

almas1992 commented Apr 8, 2025

By the way, you can retrieve the method name like goravel/app/http/controllers.(*UserController).Show-fm within the func (r *Group) Get(relativePath string, handler httpcontract.HandlerFunc) function.

For example:

func (r *Group) Get(relativePath string, handler httpcontract.HandlerFunc) {
    pc := reflect.ValueOf(handler).Pointer()
    fn := runtime.FuncForPC(pc)
    fmt.Println("fn.Name() ========>", fn.Name())

    r.getRoutesWithMiddlewares().GET(
        pathToGinPath(relativePath),
        []gin.HandlerFunc{handlerToGinHandler(handler)}...,
    )
    r.clearMiddlewares()
}

Output:

fn.Name()=========> goravel/app/http/controllers.(*UserController).Show-fm

In other words, the []route.Info should ideally be populated during route definition, since all the necessary context—including the handler’s full name—is available at that point.

If performance is a concern, one possible optimization is to store reflect.ValueOf(handler).Pointer() during route definition, and defer the more expensive call to runtime.FuncForPC until the handler name actually needs to be displayed.

@devhaozi
Copy link
Member Author

devhaozi commented Apr 8, 2025

If performance is a concern, one possible optimization is to store reflect.ValueOf(handler).Pointer() during route definition, and defer the more expensive call to runtime.FuncForPC until the handler name actually needs to be displayed.

There is no performance issue, because the routes do not need to be registered frequently. I am considering that this will require many modifications.

@devhaozi
Copy link
Member Author

devhaozi commented Apr 8, 2025

If performance is a concern, one possible optimization is to store reflect.ValueOf(handler).Pointer() during route definition, and defer the more expensive call to runtime.FuncForPC until the handler name actually needs to be displayed.

There is no performance issue, because the routes do not need to be registered frequently. I am considering that this will require many modifications.

And need to support Group route, which seems not easy too.

@almas1992
Copy link
Contributor

The required changes are indeed a bit extensive. For each routing method—such as Any, Get, Post, Put, Resource, etc.—we need to extract the handler’s function name and compute the full route path at the time of registration. This information should be stored in a global []route.Info slice to ensure all routes are collected in one place.

A global variable is necessary because if []route.Info were a field on individual structs, then route definitions—especially from grouped routes—would be scattered across multiple Group instances, making centralized tracking difficult.

Note: In Gin, computing the full route path for grouped routes requires combining both the originPrefix and prefix fields from the Group struct.

@almas1992
Copy link
Contributor

I’m wondering whether it’s necessary to build a framework built-in routing system, instead of relying on third-party libraries like Gin or Fiber. Since Go 1.22 introduced built-in routing enhancements, the standard library is now capable of handling routing natively.

This would also give us full flexibility to implement structured routing, grouping, middleware, and other features without limitations—allowing the design to better align with the philosophy of our framework. @hwbrzzl

I know this would be a massive undertaking, but as the saying goes, “The road ahead is long and arduous, I will seek far and wide.”

@hwbrzzl
Copy link
Contributor

hwbrzzl commented Apr 8, 2025

I’m wondering whether it’s necessary to build a framework built-in routing system, instead of relying on third-party libraries like Gin or Fiber. Since Go 1.22 introduced built-in routing enhancements, the standard library is now capable of handling routing natively.

This would also give us full flexibility to implement structured routing, grouping, middleware, and other features without limitations—allowing the design to better align with the philosophy of our framework. @hwbrzzl

I know this would be a massive undertaking, but as the saying goes, “The road ahead is long and arduous, I will seek far and wide.”

Yes, maybe in the future. For now, it should not be our main work, can't give us a lot of benefits.

@devhaozi
Copy link
Member Author

devhaozi commented Apr 8, 2025

I’m wondering whether it’s necessary to build a framework built-in routing system, instead of relying on third-party libraries like Gin or Fiber. Since Go 1.22 introduced built-in routing enhancements, the standard library is now capable of handling routing natively.

This would also give us full flexibility to implement structured routing, grouping, middleware, and other features without limitations—allowing the design to better align with the philosophy of our framework. @hwbrzzl

I know this would be a massive undertaking, but as the saying goes, “The road ahead is long and arduous, I will seek far and wide.”

Yes, that why I create a new package mux, I want to do some test first.

@devhaozi devhaozi closed this Apr 8, 2025
@devhaozi
Copy link
Member Author

devhaozi commented Apr 8, 2025

Due to my work schedule, I am unable to continue this PR, if anyone is willing, please feel free to take over.

@devhaozi devhaozi deleted the haozi/route branch April 8, 2025 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

http路由需要返回所有注册的路由信息
3 participants