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:
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:
And with missing parameters at http://127.0.0.1:8000/:
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.