日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
創(chuàng)新互聯(lián)GoFrame教程:GoFrame高級(jí)特性-HOOK事件回調(diào)

?ghttp.Server?提供了事件回調(diào)注冊(cè)功能,類似于其他框架的中間件功能,相比較于中間件,事件回調(diào)的特性更加簡單。

十多年的宜陽網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營銷型網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整宜陽建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)建站從事“宜陽網(wǎng)站設(shè)計(jì)”,“宜陽網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

?ghttp.Server?支持用戶對(duì)于某一事件進(jìn)行自定義監(jiān)聽處理,按照?pattern?方式進(jìn)行綁定注冊(cè)(?pattern?格式與路由注冊(cè)一致)。支持多個(gè)方法對(duì)同一事件進(jìn)行監(jiān)聽,?ghttp.Server?將會(huì)按照路由優(yōu)先級(jí)及回調(diào)注冊(cè)順序進(jìn)行回調(diào)方法調(diào)用。同一事件時(shí)先注冊(cè)的?HOOK?回調(diào)函數(shù)優(yōu)先級(jí)越高。 相關(guān)方法如下:

func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) error
func (s *Server) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error

當(dāng)然域名對(duì)象也支持事件回調(diào)注冊(cè):

func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFunc) error
func (d *Domain) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error

支持的?Hook?事件列表:

  • ?ghttp.HookBeforeServe ?

在進(jìn)入/初始化服務(wù)對(duì)象之前,該事件是最常用的事件,特別是針對(duì)于權(quán)限控制、跨域請(qǐng)求等處理。

  • ?ghttp.HookAfterServe ?

在完成服務(wù)執(zhí)行流程之后。

  • ?ghttp.HookBeforeOutput ?

向客戶端輸出返回內(nèi)容之前。

  • ?ghttp.HookAfterOutput ?

向客戶端輸出返回內(nèi)容之后。

具體調(diào)用時(shí)機(jī)請(qǐng)參考圖例所示。

回調(diào)優(yōu)先級(jí)

由于事件的綁定也是使用的路由規(guī)則,因此它的優(yōu)先級(jí)和 路由管理-路由規(guī)則 章節(jié)介紹的優(yōu)先級(jí)是一樣的。

但是事件調(diào)用時(shí)和路由注冊(cè)調(diào)用時(shí)的機(jī)制不一樣,同一個(gè)路由規(guī)則下允許綁定多個(gè)事件回調(diào)方法,該路由下的事件調(diào)用會(huì)按照優(yōu)先級(jí)進(jìn)行調(diào)用,假如優(yōu)先級(jí)相等的路由規(guī)則,將會(huì)按照事件注冊(cè)的順序進(jìn)行調(diào)用。

關(guān)于全局回調(diào)

我們往往使用綁定?/*?這樣的?HOOK?路由來實(shí)現(xiàn)全局的回調(diào)處理,這樣是可以的。但是?HOOK?執(zhí)行的優(yōu)先是最低的,路由注冊(cè)的越精確,優(yōu)先級(jí)越高,越模糊的路由優(yōu)先級(jí)越低,?/*?就屬于最模糊的路由。

為降低不同的模塊耦合性,所有的路由往往不是在同一個(gè)地方進(jìn)行注冊(cè)。例如用戶模塊注冊(cè)的?HOOK?(?/user/*?),它將會(huì)被優(yōu)先調(diào)用隨后才可能是全局的?HOOK?;如果僅僅依靠注冊(cè)順序來控制優(yōu)先級(jí),在模塊多路由多的時(shí)候優(yōu)先級(jí)便很難管理。

業(yè)務(wù)函數(shù)調(diào)用順序

建議 相同的業(yè)務(wù)(同一業(yè)務(wù)模塊) 的多個(gè)處理函數(shù)(例如: A、B、C)放到同一個(gè)HOOK回調(diào)函數(shù)中進(jìn)行處理,在注冊(cè)的回調(diào)函數(shù)中自行管理業(yè)務(wù)處理函數(shù)的調(diào)用順序(函數(shù)調(diào)用順序: A-B-C)。

雖然同樣的需求,注冊(cè)多個(gè)相同?HOOK?的回調(diào)函數(shù)也可以實(shí)現(xiàn),功能上不會(huì)有問題,但從設(shè)計(jì)的角度來講,內(nèi)聚性降低了,不便于業(yè)務(wù)函數(shù)管理。

ExitHook方法

當(dāng)路由匹配到多個(gè)?HOOK?方法時(shí),默認(rèn)是按照路由匹配優(yōu)先級(jí)順序執(zhí)行?HOOK?方法。當(dāng)在?HOOK?方法中調(diào)用?Request.ExitHook?方法后,后續(xù)的?HOOK?方法將不會(huì)被繼續(xù)執(zhí)行,作用類似?HOOK?方法覆蓋。

接口鑒權(quán)控制

事件回調(diào)注冊(cè)比較常見的應(yīng)用是在對(duì)調(diào)用的接口進(jìn)行鑒權(quán)控制/權(quán)限控制。該操作需要綁定?ghttp.HookBeforeServe?事件,在該事件中會(huì)對(duì)所有匹配的接口請(qǐng)求(例如綁定?/*?事件回調(diào)路由)服務(wù)執(zhí)行前進(jìn)行回調(diào)處理。當(dāng)鑒權(quán)不通過時(shí),需要調(diào)用?r.ExitAll()?方法退出后續(xù)的服務(wù)執(zhí)行(包括后續(xù)的事件回調(diào)執(zhí)行)。

此外,在權(quán)限校驗(yàn)的事件回調(diào)函數(shù)中執(zhí)行?r.Redirect*?方法,又沒有調(diào)用?r.ExitAll()?方法退出業(yè)務(wù)執(zhí)行,往往會(huì)產(chǎn)生?http multiple response writeheader calls?錯(cuò)誤提示。因?yàn)?r.Redirect*?方法會(huì)往返回的?header?中寫入?Location?頭;而隨后的業(yè)務(wù)服務(wù)接口往往會(huì)往?header?寫入?Content-Type/Content-Length?頭,這兩者有沖突造成的。

中間件與事件回調(diào)

中間件(?Middleware?)與事件回調(diào)(?HOOK?)是?GF?框架的兩大流程控制特性,兩者都可用于控制請(qǐng)求流程,并且也都支持綁定特定的路由規(guī)則。但兩者區(qū)別也是非常明顯的。

  1. 首先,中間件側(cè)重于應(yīng)用級(jí)的流程控制,而事件回調(diào)側(cè)重于服務(wù)級(jí)流程控制;也就是說中間件的作用域僅限于應(yīng)用,而事件回調(diào)的“權(quán)限”更強(qiáng)大,屬于?Server?級(jí)別,并可處理靜態(tài)文件的請(qǐng)求回調(diào)。
  2. 其次,中間件設(shè)計(jì)采用了“洋蔥”設(shè)計(jì)模型;而事件回調(diào)采用的是特定事件的鉤子觸發(fā)設(shè)計(jì)。
  3. 最后,中間件相對(duì)來說靈活性更高,也是比較推薦的流程控制方式;而事件回調(diào)比較簡單,但靈活性較差。

Request.URL與Request.Router

?Request.Router?是匹配到的路由對(duì)象,包含路由注冊(cè)信息,一般來說開發(fā)者不會(huì)用到。 ?Request.URL?是底層請(qǐng)求的?URL?對(duì)象(繼承自標(biāo)準(zhǔn)庫?http.Request?),包含請(qǐng)求的?URL?地址信息,特別是?Request.URL.Path?表示請(qǐng)求的?URI?地址。

因此,假如在服務(wù)回調(diào)函數(shù)中使用的話,?Request.Router?是有值的,因?yàn)橹挥衅ヅ涞搅寺酚刹艜?huì)調(diào)用服務(wù)回調(diào)方法。但是在事件回調(diào)函數(shù)中,該對(duì)象可能為?nil?(表示沒有匹配到服務(wù)回調(diào)函數(shù)路由)。特別是在使用事件回調(diào)對(duì)請(qǐng)求接口鑒權(quán)的時(shí)候,應(yīng)當(dāng)使用?Request.URL?對(duì)象獲取請(qǐng)求的?URL?信息,而不是?Request.Router?。

靜態(tài)文件事件

如果僅僅是提供?API?接口服務(wù)(包括前置靜態(tài)文件服務(wù)代理如?nginx?),不涉及到靜態(tài)文件服務(wù),那么可以忽略該小節(jié)。

需要注意的是,事件回調(diào)同樣能夠匹配到符合路由規(guī)則的靜態(tài)文件訪問(靜態(tài)文件特性在?gf?框架中是默認(rèn)開啟的,我們可以使用?WebServer?相關(guān)配置來手動(dòng)關(guān)閉。

例如,我們注冊(cè)了一個(gè)?/*?的全局匹配事件回調(diào)路由,那么?/static/js/index.js?或者?/upload/images/thumb.jpg?等等靜態(tài)文件訪問也會(huì)被匹配到,會(huì)進(jìn)入到注冊(cè)的事件回調(diào)函數(shù)中進(jìn)行處理。

我們可以在事件回調(diào)函數(shù)中使用?Request.IsFileRequest()?方法來判斷該請(qǐng)求是否是靜態(tài)文件請(qǐng)求,如果業(yè)務(wù)邏輯不需要靜態(tài)文件的請(qǐng)求事件回調(diào),那么在事件回調(diào)函數(shù)中直接忽略即可,以便進(jìn)行選擇性地處理。

事件回調(diào)示例

示例1,基本使用

package main

import (
	"github.com/GOgf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/glog"
)

func main() {
	// 基本事件回調(diào)使用
	p := "/:name/info/{uid}"
	s := g.Server()
	s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeServe:  func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeServe) },
		ghttp.HookAfterServe:   func(r *ghttp.Request) { glog.Println(ghttp.HookAfterServe) },
		ghttp.HookBeforeOutput: func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeOutput) },
		ghttp.HookAfterOutput:  func(r *ghttp.Request) { glog.Println(ghttp.HookAfterOutput) },
	})
	s.BindHandler(p, func(r *ghttp.Request) {
		r.Response.Write("用戶:", r.Get("name"), ", uid:", r.Get("uid"))
	})
	s.SetPort(8199)
	s.Run()
}

當(dāng)訪問 http://127.0.0.1:8199/john/info/10000 時(shí),運(yùn)行?WebServer?進(jìn)程的終端將會(huì)按照事件的執(zhí)行流程打印出對(duì)應(yīng)的事件名稱。

示例2,相同事件注冊(cè)

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

// 優(yōu)先調(diào)用的HOOK
func beforeServeHook1(r *ghttp.Request) {
	r.SetParam("name", "GoFrame")
	r.Response.Writeln("set name")
}

// 隨后調(diào)用的HOOK
func beforeServeHook2(r *ghttp.Request) {
	r.SetParam("site", "https://goframe.org")
	r.Response.Writeln("set site")
}

// 允許對(duì)同一個(gè)路由同一個(gè)事件注冊(cè)多個(gè)回調(diào)函數(shù),按照注冊(cè)順序進(jìn)行優(yōu)先級(jí)調(diào)用。
// 為便于在路由表中對(duì)比查看優(yōu)先級(jí),這里講HOOK回調(diào)函數(shù)單獨(dú)定義為了兩個(gè)函數(shù)。
func main() {
	s := g.Server()
	s.BindHandler("/", func(r *ghttp.Request) {
		r.Response.Writeln(r.Get("name"))
		r.Response.Writeln(r.Get("site"))
	})
	s.BindHookHandler("/", ghttp.HookBeforeServe, beforeServeHook1)
	s.BindHookHandler("/", ghttp.HookBeforeServe, beforeServeHook2)
	s.SetPort(8199)
	s.Run()
}

執(zhí)行后,終端輸出的路由表信息如下:

SERVER  | ADDRESS | DOMAIN  | METHOD | P | ROUTE |        HANDLER        |    MIDDLEWARE
|---------|---------|---------|--------|---|-------|-----------------------|-------------------|
  default |  :8199  | default | ALL    | 1 | /     | main.main.func1       |
|---------|---------|---------|--------|---|-------|-----------------------|-------------------|
  default |  :8199  | default | ALL    | 2 | /     | main.beforeServeHook1 | HOOK_BEFORE_SERVE
|---------|---------|---------|--------|---|-------|-----------------------|-------------------|
  default |  :8199  | default | ALL    | 1 | /     | main.beforeServeHook2 | HOOK_BEFORE_SERVE
|---------|---------|---------|--------|---|-------|-----------------------|-------------------|

手動(dòng)訪問 http://127.0.0.1:8199/ 后,頁面輸出內(nèi)容為:

set name
set site
GoFrame
https://goframe.org

示例3,改變業(yè)務(wù)邏輯

package main

import (
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

func main() {
	s := g.Server()
	// 多事件回調(diào)示例,事件1
	pattern1 := "/:name/info"
	s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeServe: func(r *ghttp.Request) {
			r.SetParam("uid", 1000)
		},
	})
	s.BindHandler(pattern1, func(r *ghttp.Request) {
		r.Response.Write("用戶:", r.Get("name"), ", uid:", r.Get("uid"))
	})

	// 多事件回調(diào)示例,事件2
	pattern2 := "/{object}/list/{page}.java"
	s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeOutput: func(r *ghttp.Request) {
			r.Response.SetBuffer([]byte(
				fmt.Sprintf("通過事件修改輸出內(nèi)容, object:%s, page:%s", r.Get("object"), r.GetRouterString("page"))),
			)
		},
	})
	s.BindHandler(pattern2, func(r *ghttp.Request) {
		r.Response.Write(r.Router.Uri)
	})
	s.SetPort(8199)
	s.Run()
}

通過事件1設(shè)置了訪問?/:name/info?路由規(guī)則時(shí)的?GET?參數(shù);通過事件2,改變了當(dāng)訪問的路徑匹配路由?/{object}/list/{page}.java?時(shí)的輸出結(jié)果。執(zhí)行之后,訪問以下?URL?查看效果:

  • http://127.0.0.1:8199/user/info
  • http://127.0.0.1:8199/user/list/1.java
  • http://127.0.0.1:8199/user/list/2.java

示例4,回調(diào)執(zhí)行優(yōu)先級(jí)

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

func main() {
	s := g.Server()
	s.BindHandler("/priority/show", func(r *ghttp.Request) {
		r.Response.Writeln("priority service")
	})

	s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeServe: func(r *ghttp.Request) {
			r.Response.Writeln("/priority/:name")
		},
	})
	s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeServe: func(r *ghttp.Request) {
			r.Response.Writeln("/priority/*any")
		},
	})
	s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc{
		ghttp.HookBeforeServe: func(r *ghttp.Request) {
			r.Response.Writeln("/priority/show")
		},
	})
	s.SetPort(8199)
	s.Run()
}

在這個(gè)示例中,我們往注冊(cè)了3個(gè)路由規(guī)則的事件回調(diào),并且都能夠匹配到路由注冊(cè)的地址?/priority/show?,這樣我們便可以通過訪問這個(gè)地址來看看路由執(zhí)行的順序是怎么樣的。

執(zhí)行后我們?cè)L問 http://127.0.0.1:8199/priority/show ,隨后我們看到頁面輸出以下信息:

/priority/show
/priority/:name
/priority/*any
priority service

示例5,允許跨域請(qǐng)求

?HOOK?和中間件都能實(shí)現(xiàn)跨域請(qǐng)求處理,我們這里使用?HOOK?來實(shí)現(xiàn)簡單的跨域處理。 首先我們來看一個(gè)簡單的接口示例:

package main

import (
    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/net/ghttp"
)

func Order(r *ghttp.Request) {
    r.Response.Write("GET")
}

func main() {
    s := g.Server()
    s.Group("/api.v1", func(group *ghttp.RouterGroup) {
        group.GET("/order", Order)
    })
    s.SetPort(8199)
    s.Run()
}

接口地址是 http://localhost:8199/api.v1/order ,當(dāng)然這個(gè)接口是不允許跨域的。我們打開一個(gè)不同的域名,例如:百度首頁(正好用了?jQuery?,方便調(diào)試),然后按?F12?打開開發(fā)者面板,在?console?下執(zhí)行以下?AJAX?請(qǐng)求:

$.get("http://localhost:8199/api.v1/order", function(result){
    console.log(result)
});

結(jié)果如下:

返回了不允許跨域的錯(cuò)誤,接著我們修改一下測試代碼,如下:

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

func Order(r *ghttp.Request) {
	r.Response.Write("GET")
}

func main() {
	s := g.Server()
	s.Group("/api.v1", func(group *ghttp.RouterGroup) {
		group.Hook("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
			r.Response.CORSDefault()
		})
		group.GET("/order", Order)
	})
	s.SetPort(8199)
	s.Run()
}

我們?cè)黾恿酸槍?duì)于路由?/api.v1/*any?的綁定事件?ghttp.HookBeforeServe?,該事件將會(huì)在所有服務(wù)執(zhí)行之前調(diào)用,該事件的回調(diào)方法中,我們通過調(diào)用?CORSDefault?方法使用默認(rèn)的跨域設(shè)置允許跨域請(qǐng)求。該綁定的事件路由規(guī)則使用了模糊匹配規(guī)則,表示所有?/api.v1?開頭的接口地址都允許跨域請(qǐng)求。

返回剛才的百度首頁,再次執(zhí)行請(qǐng)求?AJAX?請(qǐng)求,這次便成功了:


網(wǎng)頁名稱:創(chuàng)新互聯(lián)GoFrame教程:GoFrame高級(jí)特性-HOOK事件回調(diào)
文章來源:http://www.5511xx.com/article/dpceocc.html