基于 gorilla/mux 包实现路由定义和请求分发:路由中间件

go-http-middleware

Laravel 路由一样,Mux 也支持在路由中使用中间件,并且按照顺序匹配执行。如果你对中间件不太了解,可以先去看下我们在 Laravel 中间件文档中的简单介绍。和 Laravel 一样,在 Go Web 编程中,中间件的典型使用场景包括认证、日志、请求头操作和 ResponseWriter “劫持”等。

一个典型的 Mux 路由中间件通常通过一个闭包来定义,我们可以在闭包函数中处理传入的请求和响应实例或增加额外业务逻辑,然后调用传入的处理器继续后续请求处理(可能是下一个中间件或者最终的路由处理器)。比如,我们可以这样定义一个日志中间件:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Do stuff here
        log.Println(r.RequestURI)
        // Call the next handler, which can be another middleware in the chain, or the final handler.
        next.ServeHTTP(w, r)
    })
}

这个实现和 Laravel 中间件非常相似,通过类比的方式很容易理解:

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;

class RequestLog
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        Log::info(request()->url());
        return $next($request);
    }
}

如果要将上述 Mux 日志中间件应用到所有路由,可以通过 Use 方法实现:

r := mux.NewRouter()
r.Use(loggingMiddleware)

还可以将其批量应用到子路由,以便限定其作用范围:

postRouter := r.PathPrefix("/posts").Subrouter()
postRouter.Use(loggingMiddleware)

当然,上述日志中间件的定义非常简单,没有涉及请求处理和异常中断,我们可以仿照 Laravel 中间件文档中的 CheckToken 示例实现 Mux 版本的令牌检查中间件:

func checkToken(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Do stuff here
        token := r.FormValue("token")
        if token == "xueyuanjun.com" {
            log.Printf("Token check success: %s\n", r.RequestURI)
            // Call the next handler, which can be another middleware in the chain, or the final handler.
            next.ServeHTTP(w, r)
        } else {
            http.Error(w, "Forbidden", http.StatusForbidden)
        }
    })
}

我们将其应用到 postRouter 子路由:

postRouter := r.PathPrefix("/posts").Subrouter()
postRouter.Use(checkToken)

重新启动 Go HTTP 服务器,访问任意 posts 子路由,就会被拒绝访问:

访问被拒绝

只有传递了正确的 token 参数才可以正常访问:

通过令牌验证后可以正常访问

如果我们将日志中间件应用到全局路由器的话,此时可以在日志输出中看到所有请求的日志信息:

打印请求日志

关于 Mux 路由中间件我们就简单介绍到这里,下一篇教程,我们继续探索 Mux 路由的其它用法,比如处理静态文件和单页面应用。

上一篇: 基于 gorilla/mux 包实现路由定义和请求分发:进阶使用

下一篇: 基于 gorilla/mux 包实现路由定义和请求分发:处理静态资源响应