GoFrameWeb_Demo-1

文章详细介绍了GoFrame框架如何进行程序启动,包括通过main.go调用internal/cmd包的Main对象Run命令引导程序启动,以及HTTPServer的创建和管理。重点讨论了路由注册,如分组路由、层级注册和对象注册,展示了各种路由规则和处理函数的绑定方法,并提到了中间件的使用。此外,还涵盖了RESTful对象注册和构造方法Init与析构方法Shut的应用。
摘要由CSDN通过智能技术生成

一、程序启动

所有的程序入口都是由main.go进入,该文件主要是调用internal/cmd包的对应命令引导程序启动。在项目模板中,默认会执行internal/cmd包的Main对象Run命令引导程序启动。
// 调用internal/cmd包的对应命令引导程序启动。
// 框架的核心组件均需要传递context上下文参数,这里使用gctx.New表示创建一个带链路跟踪特性的context上下文对象给下游链路。
	cmd.Main.Run(gctx.GetInitCtx())

引导启动

Main对象的Run命令的主要作用是做引导启动

// 默认创建一个HTTP Server(s := g.Server()),然后通过分组路由的方式注册路由,并启动HTTP Server,
// 随后HTTP Server将会阻塞运行,它同时也会异步监听系统信号,直至收到退出信号后,它会优雅关闭连接随后退出进程。
var (
   Main = gcmd.Command{
      Name:  "main",
      Usage: "main",
      Brief: "start http server",
      Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
         s := g.Server()
         s.Group("/", func(group *ghttp.RouterGroup) {
            group.Middleware(ghttp.MiddlewareHandlerResponse)
            group.Bind(
               hello.New(),
            )
         })
         s.Run()
         return nil
      },
   }
)

路由注册

在web开发中,“route”是指根据url分配到对应的处理程序。

以这个页面为例:

请添加图片描述

把这一整个页面看成一个大的Group(/api),左上角的那些button可以看成子Group,然后每个子Group再对应一个页面,该页面的每个按钮又可以进行一个Group划分。

在项目模板中使用了Group方法创建了分组路由,框架的HTTP Server支持多种路由注册方式,而分组路由也是最常见的路由注册方式

- - 路由管理-路由规则(示例)–以哪种方式来定义规则

package main

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

func main() {
    // s := g.Server(): 创建一个 HTTP 服务器实例。
    s := g.Server()
    // s.BindHandler("/:name", func(r *ghttp.Request) { ... }):将处理函数绑定到路由规则 /:name 上,该规则匹配带有名为 name 的路径参数的请求。
    s.BindHandler("/:name", func(r *ghttp.Request){
       r.Response.Writeln(r.Router.Uri)
    })
    // 将处理函数绑定到路由规则 /:name/update
    s.BindHandler("/:name/update", func(r *ghttp.Request){
        r.Response.Writeln(r.Router.Uri)
    })
    s.BindHandler("/:name/:action", func(r *ghttp.Request){
        r.Response.Writeln(r.Router.Uri)
    })
    s.BindHandler("/:name/*any", func(r *ghttp.Request){
       r.Response.Writeln(r.Router.Uri)
    })
    s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request){
        r.Response.Writeln(r.Router.Uri)
    })
    s.SetPort(8199)
    s.Run()
}
— 路由管理-注册规则(示例)
  • 路由注册-分组路由
package main

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

func main() {
    // 创建一个 HTTP 服务器实例
   s := g.Server()
    //创建一个名为 /api 的路由组。
   group := s.Group("/api")
    //将处理函数绑定到路由规则 /api/all 上,该规则匹配所有 HTTP 方法的请求
   group.ALL("/all", func(r *ghttp.Request) {
      r.Response.Write("all")
   })
   // 将处理函数绑定到路由规则 /api/get 上,该规则匹配 GET 方法的请求
   group.GET("/get", func(r *ghttp.Request) {
      r.Response.Write("get")
   })
   // 将处理函数绑定到路由规则 /api/post 上,该规则匹配 POST 方法的请求。
   group.POST("/post", func(r *ghttp.Request) {
      r.Response.Write("post")
   })
   // 设置服务器的监听端口为 8199。
   s.SetPort(8199)
   s.Run()
}
其中,/api/get仅允许GET方式访问,/api/post仅允许POST方式访问,/api/all允许所有的方式访问。
是吗?是的

请添加图片描述
请添加图片描述

  • 路由注册-层级注册

GoFrame框架的分组路由注册支持更加直观优雅层级的注册方式,以便于开发者更方便地管理路由列表。路由层级注册方式也是推荐的路由注册方式。

package main

import (
   "net/http"

   "fmt"
   "github.com/gogf/gf/v2/frame/g"
   "github.com/gogf/gf/v2/net/ghttp"
   "github.com/gogf/gf/v2/util/gconv"
)
// 中间件用来判断是不是你,可以借助“钥匙开门”来理解
// 这个中间件函数用于进行认证,判断请求中是否包含正确的 token,如果 token 是 "123456",则通过调用 r.Middleware.Next() 继续执行下一个中间件或处理函数,否则返回 403 Forbidden 状态码。
func MiddlewareAuth(r *ghttp.Request) {
   token := r.Get("token")
   if gconv.String(token) == "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()
   fmt.Println(r.Response.Status, r.URL.Path)
}

func main() {
   // 创建一个 HTTP 服务器实例。
   s := g.Server()
   // 注册全局中间件函数 MiddlewareLog,用于打印请求日志信息。
   s.Use(MiddlewareLog)
   s.Group("/api.v2", func(group *ghttp.RouterGroup) {
      group.Middleware(MiddlewareAuth, MiddlewareCORS)
      group.GET("/test", func(r *ghttp.Request) {
         r.Response.Write("test")
      })
      group.Group("/order", func(group *ghttp.RouterGroup) {
         group.GET("/list", func(r *ghttp.Request) {
            r.Response.Write("list")
         })
         group.PUT("/update", func(r *ghttp.Request) {
            r.Response.Write("update")
         })
      })
      group.Group("/user", func(group *ghttp.RouterGroup) {
         group.GET("/info", func(r *ghttp.Request) {
            r.Response.Write("info")
         })
         group.POST("/edit", func(r *ghttp.Request) {
            r.Response.Write("edit")
         })
         group.DELETE("/drop", func(r *ghttp.Request) {
            r.Response.Write("drop")
         })
      })
      group.Group("/hook", func(group *ghttp.RouterGroup) {
         group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook any")
         })
         group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook name")
         })
      })
   })
   s.SetPort(8100)
   s.Run()
}

梳理一下**路由**:点击一个功能按钮,输入参数,点击运行,返回一个值。

如何测试,输入对应功能的路由地址,看看返回什么值

问题:依据某一个规则生成路由,由别人测试。依据什么规则呢?---- 就是路由规则呗

// 这个程序的主要作用是创建一个简单的 HTTP 服务器,并定义了多个路由规则和相应的处理函数。
// 它使用了 GoFrame 框架提供的功能来处理客户端的请求,并返回相应的结果。
func main() {
	s := g.Server()
	// 一个简单的分页路由示例
	// {page} 是一个路径参数,匹配在 /user/list/ 后面的任意字符串,并且以.html 结尾。当匹配成功时,请求处理函数会打印出路径参数的值。
	s.BindHandler("/user/list/{page}.html", func(r *ghttp.Request) {
		r.Response.Writeln(r.Get("page"))
	})
	// {xxx} 规则与 :xxx 规则混合使用
	// /{object}/:attr/{act}.php:这个路由规则混合了 {xxx} 和 :xxx 两种参数匹配规则。
	// {object} 是一个路径参数,匹配在路径中任意的字符串。:attr 是一个命名参数,匹配在 / 后面的任意字符串。
	// {act} 是一个路径参数,匹配以 .php 结尾的任意字符串。当匹配成功时,请求处理函数会打印出这三个参数的值。
	s.BindHandler("/{object}/:attr/{act}.php", func(r *ghttp.Request) {
		r.Response.Writeln(r.Get("object"))
		r.Response.Writeln(r.Get("attr"))
		r.Response.Writeln(r.Get("act"))
	})
	// 多种模糊匹配规则混合使用
	// {class}-{course}/:name/*act:这个路由规则混合了多种模糊匹配规则。{class} 和 {course} 都是路径参数,分别匹配以 - 分隔的任意字符串。
	// :name 是一个命名参数,匹配在 / 后面的任意字符串。*act 是一个通配符参数,匹配以 / 开头的任意路径。当匹配成功时,请求处理函数会打印出这四个参数的值。
	s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request) {
		r.Response.Writeln(r.Get("class"))
		r.Response.Writeln(r.Get("course"))
		r.Response.Writeln(r.Get("name"))
		r.Response.Writeln(r.Get("act"))
	})
	s.SetPort(8199)
	s.Run()
}

$ curl -XGET http://127.0.0.1:8199/user/list/1.html
1

$ curl -XGET http://127.0.0.1:8199/user/info/save.php
user
info
save

$ curl -XGET http://127.0.0.1:8199/class3-math/john/score
class3
math
john
score

–路由注册

在 Web 服务器中,"路由注册"是指将 URL 路径相应的处理函数处理逻辑进行关联(给url指条路,让它知道访问谁)的过程。

解释:

  1. 客户端发送请求到 Web 服务器(发过来的时候就已经按规则搞好了)

  2. 服务器根据请求的 URL 路径找到对应的路由规则

  3. 执行相应的处理函数来处理请求。

    路由注册的作用是确定请求的路径处理逻辑之间的映射关系,使服务器能够正确地路由和处理不同的请求。

假设你是一家餐厅的老板,你拥有一个网站用于接收客户的在线订餐请求。当客户访问你的网站并选择特定的菜品时,你的网站需要知道如何处理这些请求并提供正确的响应。

在这个例子中,路由注册的过程可以类比为在你的餐厅建立一张菜单。菜单上列出了不同的菜品和它们的价格。每个菜品对应着一个处理函数,用于准备并提供客户点餐所需的食物。

你的服务员会在客户到达餐厅时将菜单交给他们。当客户选择菜品并告诉服务员时,服务员会根据菜单上的信息确定该菜品的价格和制作方式。最后,服务员将订单传递给厨房,并在完成时将食物带到客户桌上。

在这个例子中:

菜单就相当于路由注册,它定义了不同菜品(URL 路径)和它们的价格(处理函数)之间的映射关系。
客户点餐就相当于客户端发送请求到服务器,请求包含了客户的选择(URL 路径)。
服务员的角色类似于服务器,它根据客户点餐的内容(URL 路径)在菜单(路由注册)中查找相应的菜品(处理函数)并进行处理。
通过路由注册,Web 服务器能够根据客户端请求的路径来确定执行的处理函数,以实现相应的功能或提供相应的服务。这样,服务器可以根据不同的路径来路由请求,执行不同的处理逻辑,并返回适当的响应给客户端。

路由注册是大老知,派活。通过路由注册,即按钮A,需要执行什么操作;按钮B,需要执行什么操作…

- - - 1、函数注册

注册的服务可以是一个实例化对象的方法地址,也可以是一个包方法地址。服务需要的数据可以通过模块内部变量形式或者对象内部变量形式进行管理,相关方法:

BindHandler(pattern string, handler interface{}) 方法的逻辑是将路由的匹配模式—pattern string 和相应的处理函数或处理逻辑—handler interface{}进行关联。当收到符合指定模式的请求时,服务器将调用指定的处理函数来处理请求。

// 通过BindHandler方法完成回调函数的注册
// handler是一个接口类型的参数,用于指定处理请求的处理函数或处理逻辑。

// BindHandler 方法的作用是将指定的路由模式和处理函数进行关联
func (s *Server) BindHandler(pattern string, handler interface{})

示例:

func main() {
   s := g.Server()
   s.BindHandler("/", func(r *ghttp.Request) {
      r.Response.Write("哈喽世界!")
   })
   s.SetPort(8199)
   s.Run()
}
---- 包方法注册

在该示例中,我们通过包方法的形式来注册路由。该方法返回总共访问的次数,由于涉及到并发安全,因此total变量使用了gtype.Int并发安全类型。执行后,当我们不停访问 http://127.0.0.1:8199/total 时,可以看到返回的数值不停递增。

var (
   total = gtype.NewInt()
)

func Total(r *ghttp.Request) {
   r.Response.Write("total:", total.Add(1))
}

func main() {
   s := g.Server()
   s.BindHandler("/total", Total)
   s.SetPort(8199)
   s.Run()
}
---- 对象方法注册
// Controller 结构体定义
type Controller struct {
   total *gtype.Int // 使用 gtype 包中的 gtype.Int 类型
}

// Total 方法定义
// 单参,无返回值,如果有返回值的话,就得写成“Total()(r *ghttp.Request)”这种形式了
func (c *Controller) Total(r *ghttp.Request) {
   r.Response.Write("total:", c.total.Add(1))
}

func main() {
   s := g.Server() // 创建一个 HTTP 服务器实例
   // 实例化
   c := &Controller{
      total: gtype.NewInt(), // 使用 gtype 包中的 gtype.NewInt() 创建一个 gtype.Int 实例
   }
   s.BindHandler("/total", c.Total) // 将路由路径 "/total" 与 Controller 结构体中的 Total 函数进行关联
   s.SetPort(8199)                  // 设置服务器监听端口为 8199
   s.Run()                          // 启动服务器并开始监听来自客户端的请求
}

使用了对象来封装业务逻辑所需的变量,使用回调函数注册来灵活注册对应的对象方法。

回调函数是一种将函数作为参数传递给其他函数,并在需要的时候被调用的机制。回调函数常用于异步编程、事件处理、扩展框架等场景中,它允许我们将某些逻辑的执行权交给调用方,使代码更加灵活和可扩展。

— 2、对象注册

对象注册注册一个实例化的对象,以后每一个请求都交给该对象(同一对象)处理,该对象常驻内存不释放

func (s *Server) BindObject(pattern string, object interface{}, methods ...string) error

// methods 是一个可变参数,用于指定允许的请求方法,如果不传递 methods 参数,则默认为允许所有请求方法。
//比如:s.BindObject("/object", c, "GET", "POST") 表示只允许 GET 和 POST 请求方法,并将其与处理对象 c 进行关联。
// 结构体定义
// 为什么要用结构体?
// 因为结构体里不只有"index",还有show
type Controller struct {
}

// Index 方法定义,用于处理 /object 路径的请求(有/object了,你想用它访问啥)
// 通过调用 r.Response.Write("index") 将字符串 "index" 写入响应中。
// ghttp.Request 是 GoFrame 框架中的类型,表示一个 HTTP 请求对象。
func (c *Controller) Index(r *ghttp.Request) {
	r.Response.Write("index")
}

// Show 方法定义
func (c *Controller) Show(r *ghttp.Request) {
	r.Response.Write("show")
}

func main() {
	// 对象在进行路由注册时便生成了一个对象s(对象在Server启动时便生成),此后不管多少请求进入,Server都是将请求转交给该对象对应的方法c进行处理。
	s := g.Server()
	// 结构体实例化
	c := new(Controller)
	// 对象注册BindObject
	s.BindObject("/object", c)
	s.SetPort(8199)
	s.Run()
}

路由内置变量(路由你的内置变量)

当使用BindObject方法进行对象注册时,在路由规则中可以使用两个内置的变量:{.struct}{.method},前者表示当前对象名称,后者表示当前注册的方法名

例子:

type Order struct{}

func (o *Order) List(r *ghttp.Request) {
   r.Response.Write("list")
}

func main() {
   s := g.Server()
   o := new(Order)
   s.BindObject("/{.struct}-{.method}", o)
   s.SetPort(8199)
   s.Run()
}

命名风格规则

type User struct {
}

func (u *User) ShowList(r *ghttp.Request) {
   r.Response.Write("list")
}
func main() {
   k := new(User)
   // 对象方法名称的路由生成方式
   s1 := g.Server("UriTypeDefault")
   s2 := g.Server("UriTypeFullName")
   s3 := g.Server("UriTypeAllLower")
   s4 := g.Server("UriTypeCamel")
   // (默认)全部转为小写,单词以'-'连接符号连接
   s1.SetNameToUriType(ghttp.UriTypeDefault)
   // 不处理名称,以原有名称构建成URI
   s2.SetNameToUriType(ghttp.UriTypeFullName)
   // 仅转为小写,单词间不使用连接符号
   s3.SetNameToUriType(ghttp.UriTypeAllLower)
   // 采用驼峰命名方式
   s4.SetNameToUriType(ghttp.UriTypeCamel)

   s1.BindObject("/{.struct}/{.method}", k)
   s2.BindObject("/{.struct}/{.method}", k)
   s3.BindObject("/{.struct}/{.method}", k)
   s4.BindObject("/{.struct}/{.method}", k)

   s1.SetPort(8100)
   s2.SetPort(8200)
   s3.SetPort(8300)
   s4.SetPort(8400)

   s1.Start()
   s2.Start()
   s3.Start()
   s4.Start()
   // 没有它还无法保持
   g.Wait()
}

对象方法注册

假如控制器中有若干公开方法,但是我只想注册其中几个,其余的方法我不想对外公开,怎么办?我们可以通过BindObject传递第三个非必需参数替换实现,参数支持传入多个方法名称,多个名称以英文,号分隔(方法名称参数区分大小写

因为**BindObject**第三个参数是一个可变参数,可接受任意参数:methods …string

type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
   r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
   r.Response.Write("show")
}

func main() {
   s := g.Server()
   c := new(Controller)
   // 不应该是Show吗,怎么是"Show"
   s.BindObject("/object", c, "Show")
   s.SetPort(8199)
   s.Run()
}
----绑定路由方法

我们可以通过BindObjectMethod方法绑定指定的路由到指定的方法执行(方法名称参数区分大小写

BindObjectMethodBindObject的区别:BindObjectMethod将对象中的指定方法与指定路由规则进行绑定(比如Show方法就给这个路由),第三个method参数只能指定一个方法名称;BindObject注册时,所有的路由都是对象方法名称按照规则生成的,第三个methods参数可以指定多个注册的方法名称(如s4.BindObject(“/{.struct}/{.method}”, k),k可以指定多个注册方法)。

type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
   r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
   r.Response.Write("show")
}

func main() {
   s := g.Server()
   c := new(Controller)
   // 这个就生成不出来"index"
   s.BindObjectMethod("/show", c, "Show")
   s.SetPort(8199)
   s.Run()
}
----RESTful对象注册

RESTful设计方式的控制器通常用于API服务。在这种模式下,HTTPMethod将会映射到控制器对应的方法名称,如:POST方式将会映射到控制器的Post方法中(公开方法,首字母大写),DELETE方式将会映射到控制器的Delete方法中…。其他非HTTP Method命名的方法,即使是定义的包公开方法,将不会自动注册,对于应用端不可见。当然,如果控制器并未定义对应HTTP Method的方法,该Method请求下将会返回 HTTP Status 404

type Controller struct{}

func (c *Controller) Get(r *ghttp.Request) {
   r.Response.Write("Get")
}
func (c *Controller) Post(r *ghttp.Request) {
   r.Response.Write("Post")
}
func (c *Controller) Delete(r *ghttp.Request) {
   r.Response.Write("Delete")
}

// 该方法无法映射到指定的方法,将会无法访问到
func (c *Controller) Hello(r *ghttp.Request) {
   r.Response.Write("Hello")
}
func main() {
   s := g.Server()
   c := new(Controller)
   s.BindObjectRest("/object", c)
   s.SetPort(8199)
   s.Run()
}

请添加图片描述

----构造方法Init与析构方法Shut

对象中的InitShut是两个在HTTP请求流程中被Server自动调用的特殊方法(类似构造函数析构函数的作用)。不会自动注册InitShut这两个方法的路由,但其中的方法可以被获取。

  1. Init回调方法

    对象收到请求时的初始化方法,在服务接口调用之前被回调执行。

    方法定义:

    // "构造函数"对象方法
    func (c *Controller) Init(r *ghttp.Request) {
    }
    
  2. Shut回调方法

    当请求结束时被Server自动调用,可以用于对象执行一些收尾处理的操作。

    方法定义:

    // "析构函数"对象方法
    func (c *Controller) Shut(r *ghttp.Request) {
    }
    

— 3、路由注册-分组路由

----分组路由

GoFrame框架支持分组路由的注册方式,可以给分组路由指定一个prefix前缀(也可以直接给定/前缀,表示注册在根路由下),在该分组下的所有路由注册都将注册在该路由前缀下。分组路由注册方式也是推荐的路由注册方式。

func main() {
   s := g.Server()
   // Group方法用于创建一个分组路由对象,并且支持在指定域名对象上创建。
   // 接下来的方法都基于它
   group := s.Group("/api")
   group.ALL("/all", func(r *ghttp.Request) {
      r.Response.Write("all")
   })
   group.GET("/get", func(r *ghttp.Request) {
      r.Response.Write("Get")
   })
   group.POST("/post", func(r *ghttp.Request) {
      r.Response.Write("Post")
   })
   s.SetPort(8199)
   s.Run()
}

请添加图片描述

---- 层级注册

妈的,输入key和value后,还是禁止我访问,怎么事???

package main

import (
   "net/http"

   "fmt"
   "github.com/gogf/gf/v2/frame/g"
   "github.com/gogf/gf/v2/net/ghttp"
   "github.com/gogf/gf/v2/util/gconv"
)

func MiddlewareAuth(r *ghttp.Request) {
   token := r.Get("token")
   if gconv.String(token) == "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()
   fmt.Println(r.Response.Status, r.URL.Path)
}

func main() {
   s := g.Server()
   s.Use(MiddlewareLog)
   s.Group("/api.v2", func(group *ghttp.RouterGroup) {
      group.Middleware(MiddlewareAuth, MiddlewareCORS)
      group.GET("/test", func(r *ghttp.Request) {
         r.Response.Write("test")
      })
      group.Group("/order", func(group *ghttp.RouterGroup) {
         group.GET("/list", func(r *ghttp.Request) {
            r.Response.Write("list")
         })
         group.PUT("/update", func(r *ghttp.Request) {
            r.Response.Write("update")
         })
      })
      group.Group("/user", func(group *ghttp.RouterGroup) {
         group.GET("/info", func(r *ghttp.Request) {
            r.Response.Write("info")
         })
         group.POST("/edit", func(r *ghttp.Request) {
            r.Response.Write("edit")
         })
         group.DELETE("/drop", func(r *ghttp.Request) {
            r.Response.Write("drop")
         })
      })
      group.Group("/hook", func(group *ghttp.RouterGroup) {
         group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook any")
         })
         group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook name")
         })
      })
   })
   s.SetPort(8199)
   s.Run()
}
   group.GET("/info", func(r *ghttp.Request) {
            r.Response.Write("info")
         })
         group.POST("/edit", func(r *ghttp.Request) {
            r.Response.Write("edit")
         })
         group.DELETE("/drop", func(r *ghttp.Request) {
            r.Response.Write("drop")
         })
      })
      group.Group("/hook", func(group *ghttp.RouterGroup) {
         group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook any")
         })
         group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
            r.Response.Write("hook name")
         })
      })
   })
   s.SetPort(8199)
   s.Run()
}

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值