Allowing Cross-Origin Requests
The first example is a common functional requirement.
We need to add a response Header
to allow cross-origin requests before all API requests. This can be achieved through middleware:
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func MiddlewareCORS(r *ghttp.Request) {
r.Response.CORSDefault()
r.Middleware.Next()
}
func main() {
s := g.Server()
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
group.ALL("/user/list", func(r *ghttp.Request) {
r.Response.Writeln("list")
})
})
s.SetPort(8199)
s.Run()
}
After execution, the terminal prints the routing table information as follows:
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
|---------|---------|---------|--------|-------------------|-------------------|---------------------|
default | default | :8199 | ALL | /api.v2/user/list | main.main.func1.1 | main.MiddlewareCORS
|---------|---------|---------|--------|-------------------|-------------------|---------------------|
Here, we use group.Middleware(MiddlewareCORS)
to register the cross-origin middleware to all service functions under the /api.v2
route in a grouped routing manner. We can then request http://127.0.0.1:8199/api.v2/user/list to see if the Header
information allowing cross-origin requests is returned.
Request Authentication Handling
We add an authentication middleware on top of the cross-origin middleware.
To simplify the example, in this demonstration, authentication is passed if the request contains a token
parameter with the value 123456
; otherwise, a 403 Forbidden
status code is returned.
package main
import (
"net/http"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func MiddlewareAuth(r *ghttp.Request) {
token := r.Get("token")
if token.String() == "123456" {
r.Response.Writeln("auth")
r.Middleware.Next()
} else {
r.Response.WriteStatus(http.StatusForbidden)
}
}
func MiddlewareCORS(r *ghttp.Request) {
r.Response.Writeln("cors")
r.Response.CORSDefault()
r.Middleware.Next()
}
func main() {
s := g.Server()
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS, MiddlewareAuth)
group.ALL("/user/list", func(r *ghttp.Request) {
r.Response.Writeln("list")
})
})
s.SetPort(8199)
s.Run()
}
After execution, the terminal prints the routing table information as follows:
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
|---------|---------|---------|--------|-------------------|-------------------|-----------------------------------------|
default | default | :8199 | ALL | /api.v2/user/list | main.main.func1.1 | main.MiddlewareCORS,main.MiddlewareAuth
|---------|---------|---------|--------|-------------------|-------------------|-----------------------------------------|
As you can see, our service method is bound with two middlewares, a cross-origin middleware and an authentication middleware. Requests will execute the MiddlewareCORS
global middleware first, followed by the MiddlewareAuth
group middleware, according to their registered order. We can then request http://127.0.0.1:8199/api.v2/user/list and http://127.0.0.1:8199/api.v2/user/list?token=123456 to compare the effects.
Handling Authentication Exceptions
Group routing middleware makes it convenient to add authentication exceptions, as only service methods registered under the current group route will bind and execute the authentication middleware; otherwise, the authentication middleware will not be executed.
package main
import (
"net/http"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func MiddlewareAuth(r *ghttp.Request) {
token := r.Get("token")
if token.String() == "123456" {
r.Middleware.Next()
} else {
r.Response.WriteStatus(http.StatusForbidden)
}
}
func main() {
s := g.Server()
s.Group("/admin", func(group *ghttp.RouterGroup) {
group.ALL("/login", func(r *ghttp.Request) {
r.Response.Writeln("login")
})
group.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth)
group.ALL("/dashboard", func(r *ghttp.Request) {
r.Response.Writeln("dashboard")
})
})
})
s.SetPort(8199)
s.Run()
}
After execution, the terminal prints the routing table information as follows:
SERVER | ADDRESS | DOMAIN | METHOD | P | ROUTE | HANDLER | MIDDLEWARE
|---------|---------|---------|--------|---|------------------|---------------------|---------------------|
default | :8199 | default | ALL | 2 | /admin/dashboard | main.main.func1.2.1 | main.MiddlewareAuth
|---------|---------|---------|--------|---|------------------|---------------------|---------------------|
default | :8199 | default | ALL | 2 | /admin/login | main.main.func1.1 |
|---------|---------|---------|--------|---|------------------|---------------------|---------------------|
We can see that only the service method of the /admin/dashboard
route is bound with the authentication middleware main.MiddlewareAuth
, whereas the service method of the /admin/login
route does not have authentication processing added. We can then visit the following URLs to see the effect:
- http://127.0.0.1:8199/admin/login
- http://127.0.0.1:8199/admin/dashboard
- http://127.0.0.1:8199/admin/dashboard?token=123456
Unified Error Handling
Based on middleware, we can perform some posterior judgments after the service function has been executed, especially for unified data format return, result processing, error judgment, etc. These requirements can be implemented using posterior middleware types. We use a simple example to demonstrate how to use middleware to do posterior judgment processing for all API requests, serving as an inspiration.
package main
import (
"net/http"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func MiddlewareAuth(r *ghttp.Request) {
token := r.Get("token")
if token.String() == "123456" {
r.Middleware.Next()
} else {
r.Response.WriteStatus(http.StatusForbidden)
}
}
func MiddlewareCORS(r *ghttp.Request) {
r.Response.CORSDefault()
r.Middleware.Next()
}
func MiddlewareErrorHandler(r *ghttp.Request) {
r.Middleware.Next()
if r.Response.Status >= http.StatusInternalServerError {
r.Response.ClearBuffer()
r.Response.Writeln("Oops, the server is taking a break, please try again later!")
}
}
func main() {
s := g.Server()
s.Use(MiddlewareCORS)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareErrorHandler)
group.ALL("/user/list", func(r *ghttp.Request) {
panic("db error: sql is xxxxxxx")
})
})
s.SetPort(8199)
s.Run()
}
After execution, the terminal prints the routing table information as follows:
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
|---------|---------|---------|--------|-------------------|---------------------|-------------------------------------------------|
default | default | :8199 | ALL | /* | main.MiddlewareCORS | GLOBAL MIDDLEWARE
|---------|---------|---------|--------|-------------------|---------------------|-------------------------------------------------|
default | default | :8199 | ALL | /api.v2/user/list | main.main.func1.1 | main.MiddlewareAuth,main.MiddlewareErrorHandler
|---------|---------|---------|--------|-------------------|---------------------|-------------------------------------------------|
In this example, we determine whether there is a system error in the posterior middleware, and if there is, we return a fixed prompt message instead of displaying sensitive error messages to the user. Of course, in real project scenarios, it is often necessary to parse data in the return buffer, such as JSON
data, and encapsulate it based on the current execution result to return a fixed data format, etc.
After executing this example, visit http://127.0.0.1:8199/api.v2/user/list?token=123456 to see the effect.
Custom Log Handling
Let's further refine the above example by outputting request logs, including status codes, to the terminal. Here we must use "global middleware" to intercept and handle all service requests, even 404
requests.
package main
import (
"net/http"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func MiddlewareAuth(r *ghttp.Request) {
token := r.Get("token")
if token.String() == "123456" {
r.Middleware.Next()
} else {
r.Response.WriteStatus(http.StatusForbidden)
}
}
func MiddlewareCORS(r *ghttp.Request) {
r.Response.CORSDefault()
r.Middleware.Next()
}
func MiddlewareLog(r *ghttp.Request) {
r.Middleware.Next()
errStr := ""
if err := r.GetError(); err != nil {
errStr = err.Error()
}
g.Log().Println(r.Response.Status, r.URL.Path, errStr)
}
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"AccessLogEnabled": false,
"ErrorLogEnabled": false,
})
s.Use(MiddlewareLog, MiddlewareCORS)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth)
group.ALL("/user/list", func(r *ghttp.Request) {
panic("Oops, I made a mistake!")
})
})
s.SetPort(8199)
s.Run()
}
As you can see, we registered a global log handling middleware and cross-origin middleware, while the authentication middleware is registered under the /api.v2
route.
After execution, we can request http://127.0.0.1:8199/api.v2/user/list and http://127.0.0.1:8199/api.v2/user/list?token=123456 to compare the effects and check the log output in the terminal.