go - 如何用中间件包装 API Endpoint
问题描述
AM 尝试生成并应用客户端凭据访问令牌来包装简单的 API 端点。当所有代码都在主函数中时,我得到的代码运行良好。但是,我希望将 API 端点(/protected)与中间件一起放在不同目录中的主函数之外。我该如何做到这一点,以便中间件适用于对服务器的每个请求?
我在父目录中的主要功能
func main() {
log.Printf("Server started")
router := sw.NewRouter()
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
manager.MustTokenStorage(store.NewMemoryTokenStore())
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
router.HandleFunc("/oauth2/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
router.HandleFunc("/credentials", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
Domain: "http://localhost:9094",
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret})
})
log.Fatal(http.ListenAndServe(":8000", router))
}
middleware API endpoint and router in a sub directory
func protecteduri(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := srv.ValidationBearerToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f.ServeHTTP(w, r)
})
}
router
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Routes []Route
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
var handler http.Handler
handler = route.HandlerFunc
handler = Logger(handler, route.Name)
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(handler)
}
return router
}
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
var routes = Routes{
{
"Index",
"GET",
"/",
Index,
},
{
"protecteduri",
strings.ToUpper("Get"),
"/protected",
protecteduri,
},
{
"AccessTokenRequest",
strings.ToUpper("Post"),
"/oauth2/token",
AccessTokenRequest,
},
}
通常我在路由器文件中添加中间件为
hadler = 中间件(处理程序)
它适用于所有传入的请求,但在这种情况下它不起作用。
完整的工作代码是这个
func main() {
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
// token memory store
manager.MustTokenStorage(store.NewMemoryTokenStore())
// client memory store
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
http.HandleFunc("/oauth2/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
http.HandleFunc("/credentials", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
Domain: "http://localhost:9094",
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret})
})
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
log.Fatal(http.ListenAndServe(":9096", nil))
}
//main function end here
//middleware function below
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := srv.ValidationBearerToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f.ServeHTTP(w, r)
})
}
如您所见,请求此句柄函数 http.HandleFunc("/protected"... 的 accesstoken 是使用中间件函数 "validateToken" 进行验证的。
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
我想要做的是通过将路由句柄函数(http.HandleFunc(“/protected”...)移动到主函数之外来重写主函数,并且当我向(http. HandleFunc("/protected", validateToken(...) 函数使用上面定义的路由。在较早的代码中,它可以工作,但是句柄函数的令牌
func protecteduri(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}
未经验证。
我将添加更多句柄函数,并且不想将它们全部放在主函数中。
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
解决方案
推荐阅读
- kivy - 当根小部件来自 .kv 文件中的实例时,Kivy 中出现错误
- java - 从静态内部类访问外部类的静态变量
- android - 从运行 Oreo 且没有 REQUEST_INSTALL_PACKAGES 的 android 应用程序安装 apk
- php - laravel 模型关系和多级角色
- wpf - 子类 ResourceDictionary 的 XAML 设计器错误“对象与目标类型不匹配”
- android - 多模块应用中的Android导航组件和BottomNavigationView
- php - 从数据库中选择日期,但在相同数据的情况下仅显示一个
- python - 如何从 python 中的列表中计算数学问题?
- android - 从 strings.xml 转移到 MainActivity.xml 时不起作用
- vue.js - 如何在 Element UI 中使用 Font Awesome 5 图标