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

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:

  1. http://127.0.0.1:8199/admin/login
  2. http://127.0.0.1:8199/admin/dashboard
  3. 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.