The GoFrame framework provides a standardized routing approach that simplifies route registration and lets developers focus on business logic. We call this Standard Routing - a more intuitive and maintainable way to handle HTTP endpoints.

Defining Request/Response Types

Standard routing uses structured types for both requests and responses. This approach enables parameter validation, documentation generation, and future extensibility:

  1. type HelloReq struct {
  2. g.Meta `path:"/" method:"get"`
  3. Name string `v:"required" dc:"User's name"`
  4. Age int `v:"required" dc:"User's age"`
  5. }
  6. type HelloRes struct {}

Key components:

  • The request struct embeds g.Meta with routing metadata tags:
    • path: The URL path to register
    • method: The HTTP method to handle (GET, POST, etc.)
  • Field tags provide additional functionality:
    • v: Validation rules (short for “valid”). Here, v:"required" marks fields as mandatory
    • dc: Documentation comments (short for “description”) for API docs

Standard Routing - 图1info

For a complete reference of available tags and validation rules, check out the relevant chapters in our development manual. For now, just focus on understanding their basic purpose.

Object-Oriented Route Handlers

For better maintainability, especially in larger APIs, we use an object-oriented approach instead of registering routes one by one. Here’s how it works:

  1. type Hello struct{}
  2. func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
  3. r := g.RequestFromCtx(ctx)
  4. r.Response.Writef(
  5. "Hello %s! Your Age is %d",
  6. req.Name,
  7. req.Age,
  8. )
  9. return
  10. }

Benefits of this approach:

  • Routes are organized into logical groups via handler objects
  • Method signatures are more idiomatic and business-focused compared to raw func(*ghttp.Request) handlers
  • The original request object is still accessible via g.RequestFromCtx when needed

Complete Example

Here’s how we’d rewrite our web server using standard routing:

main.go

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/net/ghttp"
  6. )
  7. type HelloReq struct {
  8. g.Meta `path:"/" method:"get"`
  9. Name string `v:"required" dc:"User's name"`
  10. Age int `v:"required" dc:"User's age"`
  11. }
  12. type HelloRes struct{}
  13. type Hello struct{}
  14. func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
  15. r := g.RequestFromCtx(ctx)
  16. r.Response.Writef(
  17. "Hello %s! Your Age is %d",
  18. req.Name,
  19. req.Age,
  20. )
  21. return
  22. }
  23. func main() {
  24. s := g.Server()
  25. s.Group("/", func(group *ghttp.RouterGroup) {
  26. group.Bind(
  27. new(Hello),
  28. )
  29. })
  30. s.SetPort(8000)
  31. s.Run()
  32. }

Key points:

  • s.Group creates a route group with a common prefix (“/“ in this case)
  • group.Bind registers all public methods of our handler object automatically, using struct metadata to configure routes

Testing the API

Let’s test with valid parameters at http://127.0.0.1:8000/?name=john&age=18:

img.png

When we try an invalid request at http://127.0.0.1:8000/, we get no output. This is because the validation failed before reaching our handler - the server rejected the request due to missing required parameters.

What’s Next?

While we’ve improved our routing structure, we still need better control over error handling. For example, how can we:

  • Capture and customize validation error messages?
  • Format error responses consistently?

We’ll tackle these questions in the next chapter.