Skip to main content
Version: 2.8.x(Latest)

Remember the question from our previous chapter about handling error objects? Let's solve it by exploring the Web Server's middleware feature.

Understanding Middleware

Middleware acts as an interceptor in your web server, allowing you to process requests both before and after they reach your route handlers. You can use middleware to modify requests, validate data, handle errors, or transform responses.

Middleware functions look similar to regular route handlers but include a special Middleware object for controlling request flow. There are two types:

  • Pre-middleware: Executes before the route handler
  • Post-middleware: Executes after the route handler

Here's the basic structure:

func Middleware(r *ghttp.Request) {
// Pre-middleware logic
r.Middleware.Next()
// Post-middleware logic
}

The r.Middleware.Next() call passes control to the next handler in the chain. If you skip this call, the chain stops and returns immediately (useful for authentication middleware that needs to block unauthorized requests).

Implementing Error Handling

Let's enhance our previous example with middleware:

main.go
package main

import (
"context"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

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

type Hello struct{}

func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
r := g.RequestFromCtx(ctx)
r.Response.Writef(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
)
return
}

func ErrorHandler(r *ghttp.Request) {
r.Middleware.Next()

if err := r.GetError(); err != nil {
r.Response.Write("Error: ", err.Error())
return
}
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ErrorHandler)
group.Bind(
new(Hello),
)
})
s.SetPort(8000)
s.Run()
}

Key changes:

  • We've added an ErrorHandler middleware that checks for errors after the route handler executes
  • The middleware is applied to all routes in the group via group.Middleware(ErrorHandler)

Testing the Changes

When you run the server, you'll see:

2024-11-06 22:30:06.927 [INFO] pid[35434]: http server started listening on [:8000]
2024-11-06 22:30:06.927 [INFO] {905637567a67051830833b2189796dda} openapi specification is disabled

ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|--------|-------|-------------------|--------------------
:8000 | GET | / | main.(*Hello).Say | main.ErrorHandler
----------|--------|-------|-------------------|--------------------

Notice that our ErrorHandler is now listed in the middleware column.

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

img.png

And with missing parameters at http://127.0.0.1:8000/:

img_4.png

Looking Ahead

We've seen how middleware can elegantly handle error cases and customize error messages. But this is just scratching the surface of what middleware can do.

Consider a common scenario: most APIs in a project typically return responses in a consistent format (like JSON). Could we use middleware to standardize this across all our endpoints? Absolutely! That's exactly what we'll explore in the next chapter.