potgo

package module
v0.0.0-...-667eb86 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2020 License: MIT Imports: 26 Imported by: 0

README

Potgo

Potgo 是一个 Go 微框架,可以帮助您快速编写简单但功能强大的 Web 应用程序和 API。

安装

下载并安装

$ go get github.com/icodechef/potgo

在代码中导入

import "github.com/icodechef/potgo"

Quick start

创建 main.go

package main

import (
	"github.com/icodechef/potgo"
)

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		return c.Text("hello world!")
	})

	app.Run(":8080")
}

运行这段代码并在浏览器中访问 http://localhost:8080

$ go run main.go
Listening on: http://localhost:8080. Press CTRL+C to shut down.

路由

在讲解路由前,我们了解一下 HandlerFuncContext 上下文

HandlerFunc

一个 HandlerFunc,用于处理请求的函数

type HandlerFunc func(*Context) error
Context 上下文

HandlerFunc 传入的参数为从 sync.Pool 中获取一个新上下文 Context 对象 。

基本路由

构建基本路由只需要一个 路由路径 与一个 HandlerFunc

func main() {
	app := potgo.New()

	app.GET("/hello", func(c *potgo.Context) error {
		return c.Text("hello world!")
	})
	// ...
}
可用的路由方法

路由器允许你注册能响应任何 HTTP 请求的路由如下:

func handler(c *potgo.Context) error {
	return c.Text("Hello from method: %s and path: %s", c.Request.Method, c.Request.URL.Path)
}

func main() {
	app := potgo.New()

	// GET 路由
	app.GET("/", handler)

	// POST 路由
	app.POST("/", handler)

	// PUT 路由
	app.PUT("/", handler)

	// DELETE 路由
	app.DELETE("/", handler)

	// PATCH 路由
	app.PATCH("/", handler)

	// OPTIONS 路由
	app.OPTIONS("/", handler)

	// HEAD 路由
	app.HEAD("/", handler)
}

有时候可能需要注册一个可响应多个 HTTP 请求的路由,这时你可以使用 Match 方法,也可以使用 Any 方法注册一个实现响应所有 HTTP 请求的路由:

func main() {
	// 响应多个 HTTP 请求的路由
	app.Match("GET,POST", "/", handlerFunc)

	// 用于所有 HTTP 方法
	app.Any("/", handlerFunc)
}
路由参数

有时需要在路由中捕获一些 URL 片段。例如,从 URL 中捕获用户的 ID,可以通过定义路由参数来执行此操作:

func main() {
	app := potgo.New()

	app.GET("/user/{id}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("id"))
	})

	// ...
}

也可以根据需要在路由中定义多个参数:

func main() {
	app := potgo.New()

	app.GET("/user/{uid}/posts/{pid}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("uid") + ", Post: " + c.Param("pid"))
	})

	// ...
}

路由参数都被放在 {} 内,如果没有设置正则约束,参数名称为括号内的字面量,所以 {param}param 表示参数名称。

正则约束

可以在路由参数中约束参数的格式。{} 接受以 : 分隔的参数名称和定义参数应如何约束的正则表达式,格式为 {param:regex}

func main() {
	app := potgo.New()

	app.GET("/user/{name:[A-Za-z]+}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("name"))
	})

	app.GET("/user/{id:[0-9]+}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("id"))
	})

	// ...
}
匹配剩余字符

当已经匹配一部分 URL 片段,可以使用带 * 号路由参数匹配剩余 URL 片段,格式为 {param:*}

func main() {
	app := potgo.New()

	app.GET("/user/{id}/{action:*}", func(c *potgo.Context) error {
		return c.Text("Action: " + c.Param("action"))
	})

	// ...
}
路由命名

路由命名可以为指定路由生成 URL 或者重定向。通过在路由定义上链式调用 Name 方法可指定路由名称:

func main() {
	app := potgo.New()

	app.GET("/user/{id}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("id"))
	}).Name("user.profile")

	hello := app.GET("/hello", func(c *potgo.Context) error {
		return c.Text("Hello world!")
	})
	hello.Name("hello")
	
	app.Run(":8080")
}

注意:路由命名必须是唯一的

路由分组

一组路由可以用前缀路径进行分组,组之间共享相同的中间件和视图布局,组内可以嵌套组。

使用 Group 方法进行路由分组:

func main() {
	app := potgo.New()

	v1 := app.Group("/v1")
	{
		v1.GET("/login", func(c *potgo.Context) error {
			return c.Text("v1.login")
		})

		v1.GET("/submit", func(c *potgo.Context) error {
			return c.Text("v1.submit")
		})
	}

	v2 := app.Group("/v2")
	{
		v2.GET("/login", func(c *potgo.Context) error {
			return c.Text("v2.login")
		})

		v2.GET("/submit", func(c *potgo.Context) error {
			return c.Text("v2.submit")
		})
	}

	app.Run(":8080")
}

中间件

定义中间件

中间件的定义与路由的 HandlerFunc 一致,处理的输入是 Context 对象。

func Hello() potgo.HandlerFunc {
	return func(c *potgo.Context) error {
		err := c.Next()
		return err
	}
}

c.Next() 表示等待执行其他的中间件或用户的 HandlerFunc

使用中间件

使用 Use 方法注册中间件

import (
	"github.com/icodechef/potgo"
	"log"
)

// Logger 自定义日志访问中间件
func Logger() potgo.HandlerFunc {
	return func(c *potgo.Context) error {
		log.Println("开始记录")
		err := c.Next()
		log.Println("记录结束")
		return err
	}
}

func main() {
	app := potgo.New()

	app.Use(Logger())

	app.GET("/", func(c *potgo.Context) error {
		log.Println("访问中")
		return c.Text("Hello World")
	})

	app.Run(":8080")
}

运行这段代码并在浏览器中访问 http://localhost:8080,然后查看控制台可以得到以下输出:

$ go run main.go
Listening on: http://localhost:8080. Press CTRL+C to shut down.
2020/09/23 00:27:55 开始记录
2020/09/23 00:27:55 访问中
2020/09/23 00:27:55 记录结束
路由分组中间件
package main

import (
	"github.com/icodechef/potgo"
	"log"
	"net/http"
)

func Logger() potgo.Handler {
	return func(c *potgo.Context) error {
		log.Println("开始记录")
		err := c.Next()
		log.Println("记录结束")
		return err
	}
}

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		log.Println("访问 / 中")
		return c.Text("Hello World")
	})

	api := app.Group("/api")
	api.Use(Logger())
	{
		api.GET("/users", func(c *potgo.Context) error {
			log.Println("访问 /api/users 中")
			return c.Text("Hello, Pot")
		})
	}

	app.Run(":8080")
}

运行这段代码并在浏览器中分别访问 http://localhost:8080http://localhost:8080/api/users,然后查看控制台可以得到以下输出:

$ go run main.go
Listening on: http://localhost:8080. Press CTRL+C to shut down.
2020/09/23 00:34:39 访问 / 中
2020/09/23 00:34:58 开始记录
2020/09/23 00:34:58 访问 /api/users 中
2020/09/23 00:34:58 记录结束
前置 & 后置 中间件

中间件是在请求之前或之后执行,取决于中间件本身,也就是说 c.Next() 的位置。

func Logger() potgo.HandlerFunc {
	return func(c *potgo.Context) error {
		// 处理当前中间件的逻辑
		log.Println("记录中")

		// 处理下一个中间件
		err := c.Next()
		return err
	}
}

调整一下顺序:

func Logger() potgo.HandlerFunc {
	return func(c *potgo.Context) error {
		// 先处理下一个中间件
		err := c.Next()
		
		// 然后再处理当前中间件的逻辑
		log.Println("记录中")
		return err
	}
}

请求

接收请求

路由处理程序(HandlerFunc)可以通过 Context.Request 获取请求信息,实际上 Context.Request 就是 *http.Request

例如,获取请求 URL 信息:

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		host := c.Request.URL.Host
		path := c.Request.URL.Path
		method := c.Request.Method
		// ...
	})

	app.Run(":8080")
}
获取路径中的参数
func main() {
    app := potgo.New()
    
    app.GET("/users/{id}", func(c *potgo.Context) error {
        return c.Text("User: " + c.Param("id"))
    })
    
    app.GET("/users/{uid}/posts/{pid}", func(c *potgo.Context) error {
        uid := c.Param("uid")
        pid := c.Param("pid")
        return c.Text("User: " + uid + " Post: " + pid)
    })
    
    app.Run(":8080")
}
获取查询字符串参数

通过 Query 方法获取查询字符串参数

func main() {
    app := potgo.New()
    
    // 请求的地址为:/hello?name=world
    app.GET("/hello", func(c *potgo.Context) error {
        return c.Text("Hello, " + c.Query("name"))
    })
    
    app.Run(":8080")
}

如果查询字符串参数不存在时,可以通过 QueryDefault 方法的第二个参数指定默认值

func main() {
	app := potgo.New()

	// 请求的地址为:/hello
	app.GET("/hello", func(c *potgo.Context) error {
		return c.Text("Hello, " + c.QueryDefault("name", "world"))
	})

	app.Run(":8080")
}
获取 POST 参数

通过 PostValue 方法获取 POST 参数,注意,此方法会忽略查询字符串参数

func main() {
    app := potgo.New()
    
    app.POST("/login", func(c *potgo.Context) error {
        username := c.PostValue("username")
        password := c.PostValue("password")
        //
    })
}

同样,POST 参数不存在时,可以通过 PostValueDefault 方法的第二个参数指定默认值。

获取表单参数

通过 FormValue 方法获取表单参数

func main() {
    app := potgo.New()
    
    app.POST("/login", func(c *potgo.Context) error {
        username := c.FormValue("username")
        password := c.FormValue("password")
        //
    })
}

同样,表单参数不存在时,可以通过 FormValueDefault 方法的第二个参数指定默认值。

存储上传文件
上传单个文件

创建 index.html

<!-- 此文件位置 public/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>上传单个文件</title>
</head>
<body>
<h1>上传单个文件</h1>

<form action="/upload" method="post" enctype="multipart/form-data">
    Files: <input type="file" name="file"><br>
    <input type="submit" value="Submit">
</form>
</body>
</html>

创建路由,使用 FormFile 方法访问上传的单文件,然后使用 store 方法把上传文件移动到指定的目录

func main() {
	app := potgo.New()

	app.Static("/", "./public") // 访问 index.html

	app.POST("/upload", func(c *potgo.Context) error {
		f, _ := c.FormFile("file")
		f.Store("./uploads")
		return c.Text("File: %s uploaded!", f.File.Filename)
	})

	app.Run(":8080")
}

如果不想自动生成文件名,那么可以使用 StoreAs 方法,它接受路径和文件名作为其参数

func main() {
	app := potgo.New()

	app.Static("/", "./public") // 访问 index.html

	app.POST("/upload", func(c *potgo.Context) error {
		f, _ := c.FormFile("file")
		f.StoreAs("./uploads", "test.csv")
		return c.Text("File: %s uploaded!", f.File.Filename)
	})

	app.Run(":8080")
}
上传多个文件

创建 index.html

<!-- 此文件位置 public/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>上传多个文件</title>
</head>
<body>
<h1>上传多个文件</h1>

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="files" multiple><br>
    <input type="submit" value="Submit">
</form>
</body>
</html>

创建路由,使用 FormFiles 方法访问上传的多文件,然后使用 store 方法把上传文件移动到指定的目录

func main() {
	app := potgo.New()

	app.Static("/", "./public") // 访问 index.html

	app.POST("/upload", func(c *potgo.Context) error {
		f, _ := c.FormFiles("files")
		f.Store("./uploads")
		return c.Text("%d files uploaded!", len(f.Files))
	})

	app.Run(":8080")
}

实际上 FormFiles 方法返回的对象包含一个 Files 切片,此切片的值与 FormFile 方法的返回值一样。

func main() {
	app := potgo.New()

	app.Static("/", "./public") // 访问 index.html

	app.POST("/upload", func(c *potgo.Context) error {
		f, _ := c.FormFiles("files")

		for _, file := range f.Files {
			if _, err := file.Store("./uploads"); err != nil {
				c.Status(http.StatusBadRequest)
				return c.Text("upload file err: %s", err.Error())
			}
		}

		return c.Text("%d files uploaded!", len(f.Files))
	})

	app.Run(":8080")
}
绑定请求参数
type Account struct {
	Username  string    `http:"username"`
	Password  string    `http:"password"`
}

func main() {
	app := potgo.New()

	app.Static("/", "./public")

	app.POST("/login", func(c *potgo.Context) error {
		var account Account
		if err := c.ReadForm(&account); err != nil {
			return err
		}

		log.Println(account.Username)
		log.Println(account.Password)

		return nil
	})

	app.Run(":8080")
}

响应

路由处理程序(HandlerFunc)可以通过 Context.Response 设置响应。 Context.Response 包含 http.ResponseWriter,通过 Context.Response.Writer 进行访问。

设置 HTTP 状态码
func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		c.Status(http.StatusOK)
		// 或者
		// c.Response.WriteHeader(http.StatusOK)
		return c.Text("Hello world!")
	})

	app.Run(":8080")
}
添加响应头
func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		c.Header("Content-Type", "text/plain; charset=utf-8")
		c.Header("X-Header", "Header Value")
		return c.Text("Hello world!")
	})

	app.Run(":8080")
}

或者,你可以使用 Context.WithHeaders 方法来指定要添加到响应的头映射:

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		c.WithHeaders(map[string]string{
			"Content-Type": "text/plain; charset=utf-8",
			"X-Header": "Header Value",
		})
		return c.Text("Hello world!")
	})

	app.Run(":8080")
}
重定向

如果要重定向到另一个指定的 URL,可以使用 Context.Redirect 方法。默认情况, HTTP 状态码是 302

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		return c.Redirect("/login")
	})

	app.GET("/login", func(c *potgo.Context) error {
		return c.Text("Login")
	})

	app.Run(":8080")
}
重定向到命名路由

一旦为路由指定了名称,就可以使用 Context.RouteRedirect 重定向到该路由

func main() {
	app := potgo.New()

	app.GET("/user/{id}", func(c *potgo.Context) error {
		return c.Text("User: " + c.Param("id"))
	}).Name("user") // 命名路由

	app.GET("/", func(c *potgo.Context) error {
		return c.RouteRedirect("user", "id", 10)
	})

	app.Run(":8080")
}

设置 cookie

func main()  {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		c.SetCookie(&http.Cookie{
			Name: "foo",
			Value: "bar",
			Expires: time.Now().Add(24 * time.Hour),
		})
		return c.Text("hello world!")
	})

	app.Run(":8080")
}

获取 cookie

func main()  {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		foo, _ := c.GetCookie("foo")
		return c.Text(foo)
	})

	app.Run(":8080")
}
视图响应
func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.GET("/greet", func(c *potgo.Context) error {
		return c.View("greeting.html")
	})

	app.Run(":8080")
}
JSON 响应
func main() {
	app := potgo.New()

	app.GET("/api", func(c *potgo.Context) error {
		return c.JSON(potgo.Map{
			"lang": "golang",
			"city": "gz",
		})
	})

	app.Run(":8080")
}
文本响应
func main() {
	app := potgo.New()

	app.GET("/hello", func(c *potgo.Context) error {
		return c.Text("Hello world!")
	})

	app.Run(":8080")
}
文件响应

File 方法用于直接在用户浏览器显示一个图片之类的文件,而不是下载。

func main() {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		return c.File("./greeting.txt")
	})

	app.Run(":8080")
}
文件下载
func main() {
	app := potgo.New()

	app.GET("/download", func(c *potgo.Context) error {
		return c.Attachment("./data/fruits.csv", "fruits-02.csv")
	})

	app.Run(":8080")
}
流下载
func main() {
	app := potgo.New()

	app.GET("/streamdownload", func(c *potgo.Context) error {
		f, err := os.Open("./data/fruits.csv")
		if err != nil {
			return err
		}
		return c.StreamAttachment(f, "fruits-01.csv")
	})

	app.Run(":8080")
}

视图

创建视图

Potgo 的视图功能,默认使用 Go 语言的模板引擎库 html/template

使用 Potgo.HTML(视图目录, 文件后缀) 创建一个视图实例,然后使用 RegisterView(ViewEngine) 方法注册视图并预编译模板。

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	// ...
	
	app.Run(":8080")
}

要渲染或执行视图,在路由处理程序(Handler)中使用 Context.View 方法。

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		return c.View("welcome.html", map[string]interface{} {
			"name": "World",
		})
	})

	app.Run(":8080")
}

如你所见, 传递给 Context.View 方法的第一个参数对应 ./views 目录中视图文件的名称。第二个参数是可供视图使用的数据映射。

编写视图文件 welcome.html

<!-- 此文件位置 views/welcome.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>

<h1>Hello, {{.name}}</h1>

</body>
</html>

运行这段代码并在浏览器中访问 http://localhost:8080

渲染的结果将如下所示:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>

<h1>Hello, World</h1>

</body>
</html>
向视图传递参数

正如在前面的示例中所看到的,可以将一组数据传递给视图:

c.View("welcome.html", map[string]interface{} {
    "name": "World",
})

您还可以使用 Context.ViewData 方法将参数添加到视图中:

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		c.ViewData("name", "World")
		return c.View("welcome.html")
	})

	app.Run(":8080")
}
自定义渲染分隔符

默认分隔符为 {{}}

编写 welcome.html

<!-- 此文件位置 views/welcome.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>

<h1>Hello, <{.name}}></h1>

</body>
</html>

编写 main.go,使用 Delims 方法自定义渲染分隔符

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	view.Delims("<{", "}>")
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		c.ViewData("name", "World")
		return c.View("welcome.html")
	})

	app.Run(":8080")
}
自定义模板函数

编写 welcome.html

<!-- 此文件位置 views/welcome.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>

<h1>{{.name | greet}}</h1>

</body>
</html>

编写 main.go,使用 AddFunc 方法添加自定义模板函数

func greet(s string) string {
	return "Hello, " + s + "!"
}

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	view.AddFunc("greet", greet)
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		c.ViewData("name", "World")
		return c.View("welcome.html")
	})

	app.Run(":8080")
}
视图布局

大多数 web 应用在不同的页面中使用相同的布局方式,因此我们使用布局视图来重复使用。

创建布局视图 layout.html

<!-- 此视图文件位置 views/layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
{{ content }}
</body>
</html>

创建布局视图嵌套的 content 视图 welcome.html

<!-- 此视图文件位置 views/welcome.html -->
<h1>Hello, {{ .name }}</h1>

使用 Layout 方法设定视图布局

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	view.Layout("layout.html")
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		c.ViewData("name", "World")
		return c.View("welcome.html")
	})

	app.Run(":8080")
}

运行这段代码并在浏览器中访问 http://localhost:8080

渲染的结果将如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<h1>Hello, World</h1>
</body>
</html>

生成 URL

生成指定路由的 URL
func main() {
	app := potgo.New()

	app.GET("/users/{id}", func(c *potgo.Context) error {
		c.URL("user", "id", 10) // 返回 /user/10
		return nil
	}).Name("user")

	app.URL("user", "id", 10) // 返回 /users/10
	
	// ...
}

URL 方法第一个参数为 路由名称。如果是有定义参数的路由,可以把参数作为 URL 方法的第二个参数开始以键值对形式传入,格式为 参数键, 参数值, 参数键, 参数值...,指定的参数将会自动插入到 URL 中对应的位置。

在视图中生成 URL

route 是内置模板函数,用于生成指定路由的 URL

编辑 main.go

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.GET("/", func(c *potgo.Context) error {
		return c.View("users.html")
	})

	app.GET("/user/{id}", func(c *potgo.Context) error {
		return c.Text(c.Request.URL.String())
	}).Name("user.profile") // 路由命名

	app.Run(":8080")
}

创建视图 users.html

<!-- 此视图文件位置 views/users.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Dashboard</title>
</head>
<body>
<a href="{{ route "user.profile" "id" 10 }}">User 10</a>
</body>
</html>

运行这段代码并在浏览器中访问 http://localhost:8080

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Dashboard</title>
</head>
<body>
<a href="/user/10">User 10</a>
</body>
</html>

错误处理

页面未找到

使用 NotFound 自定义错误处理程序

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.NotFound(func(c *potgo.Context) error {
		return c.View("404.html")
	})

	app.Run(":8080")
}
HandlerFunc 错误处理

使用 Error 自定义错误处理程序

func main() {
	app := potgo.New()

	view := potgo.HTML("./views", ".html")
	app.RegisterView(view)

	app.Error(func(c *potgo.Context, message string, code int) {
		c.Status(code)
		c.ViewData("message", message)
		c.View("500.html")
	})

	app.GET("/", func(c *potgo.Context) error {
		return errors.New("Oops!")
	})

	app.Run(":8080")
}

优雅停止

带优雅停止的启动

func main()  {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		time.Sleep(5 * time.Second)
		return c.Text("Hello world!")
	})

	app.RunWithGracefulShutdown(":8080", 10 * time.Second)
}

自定义优雅停止

func main()  {
	app := potgo.New()

	app.GET("/", func(c *potgo.Context) error {
		time.Sleep(5 * time.Second)
		return c.Text("Hello world!")
	})

	srv := &http.Server{
		Addr:    ":8080",
		Handler: app,
	}
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	potgo.GracefulShutdown(srv,  10 * time.Second)
}

Documentation

Index

Constants

View Source
const NoLayout = "no.layout"

NoLayout 不使用视图布局常量

Variables

This section is empty.

Functions

func GracefulShutdown

func GracefulShutdown(srv *http.Server, timeout time.Duration)

GracefulShutdown 优雅停止

Types

type Application

type Application struct {
	Router // 内嵌类型
	// contains filtered or unexported fields
}

Application 负责管理应用程序

func New

func New() *Application

New 创建一个新的 Application

func (*Application) Error

func (app *Application) Error(handler ErrorHandlerFunc)

NotFound 添加错误处理程序

func (*Application) NotFound

func (app *Application) NotFound(handler HandlerFunc)

NotFound 添加 NotFound 处理程序

func (*Application) RegisterView

func (app *Application) RegisterView(view ViewEngine) error

RegisterView 注册视图

func (*Application) Run

func (app *Application) Run(addr string) error

Run http.ListenAndServe(addr, app) 的快捷方式

func (*Application) RunTLS

func (app *Application) RunTLS(addr, certFile, keyFile string) error

RunTLS http.ListenAndServeTLS(addr, certFile, keyFile, app) 的快捷方式

func (*Application) RunWithGracefulShutdown

func (app *Application) RunWithGracefulShutdown(addr string, timeout time.Duration)

RunWithGracefulShutdown 带优雅停止的启动

func (*Application) ServeHTTP

func (app *Application) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP 处理 HTTP 请求

func (*Application) URL

func (app *Application) URL(name string, pairs ...interface{}) string

URL 使用命名的路由和参数值创建 URL

type Context

type Context struct {
	Request  *http.Request
	Response Response
	// contains filtered or unexported fields
}

Context 上下文对象

func (*Context) Abort

func (c *Context) Abort()

Abort 跳过与当前路由关联的其它 HandlerFunc

func (*Context) Attachment

func (c *Context) Attachment(filepath, filename string) error

Attachment 文件下载

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP 返回用户 IP

func (*Context) ContentType

func (c *Context) ContentType(value string)

ContentType 设置 Content-Type

func (*Context) File

func (c *Context) File(filepath string) error

File 读取文件内容并输出

func (*Context) FormFile

func (c *Context) FormFile(name string) (*UploadedFile, error)

FormFile 返回给定键的上传文件的 UploadedFile

func (*Context) FormFiles

func (c *Context) FormFiles(name string) (*UploadedFiles, error)

FormFiles 返回给定键的上传文件的 UploadedFiles

func (*Context) FormValue

func (c *Context) FormValue(key string) string

FormValue 获取表单参数的第一个值

func (*Context) FormValueDefault

func (c *Context) FormValueDefault(key string, defaultValue string) string

FormValueDefault 获取表单参数的第一个值,如果参数不存在可以返回指定的默认值

func (*Context) FormValueMap

func (c *Context) FormValueMap(key string) map[string]string

FormValueMap 获取表单参数映射

func (*Context) FormValueSlice

func (c *Context) FormValueSlice(key string) []string

FormValueSlice 获取表单参数切片

func (*Context) Get

func (c *Context) Get(key string) (value interface{}, exists bool)

Get 从上下文中检索数据

func (*Context) GetCookie

func (c *Context) GetCookie(name string) (string, error)

GetCookie 获取 cookie

func (*Context) Header

func (c *Context) Header(name string, value string)

Header 设置 HTTP Header

func (*Context) IsAborted

func (c *Context) IsAborted() bool

IsAborted 是否已经中止

func (*Context) JSON

func (c *Context) JSON(obj interface{}) (err error)

JSON 将指定结构作为 JSON 写入响应主体

func (*Context) Layout

func (c *Context) Layout(layout string)

Layout 设置当前 HandlerFunc 使用的视图布局文件

func (*Context) MultipartForm

func (c *Context) MultipartForm() (*multipart.Form, error)

MultipartForm 取得 multipart/form-data 编码的表单数据

func (*Context) Next

func (c *Context) Next() error

Next 调用与当前路由关联的其它 HandlerFunc

func (*Context) NoLayout

func (c *Context) NoLayout()

NoLayout 设置当前 HandlerFunc 不使用视图布局

func (*Context) Param

func (c *Context) Param(key string) string

Param 获取路径中的参数

func (*Context) PostValue

func (c *Context) PostValue(key string) string

PostValue 获取 POST 参数的第一个值

func (*Context) PostValueDefault

func (c *Context) PostValueDefault(key string, defaultValue string) string

PostValueDefault 获取 POST 参数的第一个值,如果参数不存在可以返回指定的默认值

func (*Context) PostValueMap

func (c *Context) PostValueMap(key string) map[string]string

PostValueMap 获取 POST 参数映射

func (*Context) PostValueSlice

func (c *Context) PostValueSlice(key string) []string

PostValueSlice 获取 POST 参数切片

func (*Context) Query

func (c *Context) Query(key string) string

Query 获取查询字符串参数的第一个值

func (*Context) QueryDefault

func (c *Context) QueryDefault(key string, defaultValue string) string

QueryDefault 获取查询字符串参数的第一个值,如果参数不存在可以返回指定的默认值

func (*Context) QueryMap

func (c *Context) QueryMap(key string) map[string]string

QueryMap 获取查询字符串参数映射

func (*Context) QuerySlice

func (c *Context) QuerySlice(key string) []string

QuerySlice 获取查询字符串参数切片

func (*Context) ReadForm

func (c *Context) ReadForm(ptr interface{}) error

ReadForm 从 HTTP 请求中提取数据填充给定的结构体的各个字段

func (*Context) Redirect

func (c *Context) Redirect(path string, code ...int) error

Redirect 重定向

func (*Context) RouteRedirect

func (c *Context) RouteRedirect(name string, pairs ...interface{}) error

RouteRedirect 重定向到命名路由

func (*Context) Set

func (c *Context) Set(key string, value interface{})

Set 在上下文中保存数据

func (*Context) SetCookie

func (c *Context) SetCookie(cookie *http.Cookie)

SetCookie 设置 cookie

func (*Context) Status

func (c *Context) Status(code int)

Status 设置 HTTP 状态码(HTTP Status Code)

func (*Context) StreamAttachment

func (c *Context) StreamAttachment(r io.Reader, filename string) (err error)

StreamAttachment 流下载

func (*Context) Text

func (c *Context) Text(format string, data ...interface{}) (err error)

Text 将给定的字符串写入响应主体

func (*Context) URL

func (c *Context) URL(name string, pairs ...interface{}) string

URL 使用命名的路由和参数值创建 URL

func (*Context) View

func (c *Context) View(name string, optionalData ...map[string]interface{}) error

View 渲染视图

func (*Context) ViewData

func (c *Context) ViewData(key string, value interface{})

ViewData 添加视图数据

func (*Context) WithHeaders

func (c *Context) WithHeaders(headers map[string]string)

WithHeaders 添加 HTTP 头

func (*Context) Write

func (c *Context) Write(b []byte) (int, error)

Write 向客户端写入数据,c.Response.Writer.Write(b) 的快捷方式

func (*Context) WriteWithStatus

func (c *Context) WriteWithStatus(statusCode int, b []byte) (int, error)

WriteWithStatus 发送HTTP状态,并向客户端写入数据

type ErrorHandlerFunc

type ErrorHandlerFunc func(*Context, string, int)

ErrorHandlerFunc 用于错误处理的函数

func ErrorHandler

func ErrorHandler() ErrorHandlerFunc

ErrorHandler 默认错误处理程序

type HTMLEngine

type HTMLEngine struct {
	// contains filtered or unexported fields
}

HTMLEngine HTML 引擎

func HTML

func HTML(path string, extension string) *HTMLEngine

HTML 创建 HTML 对象

func (*HTMLEngine) AddFunc

func (v *HTMLEngine) AddFunc(name string, callable interface{})

AddFunc 添加视图函数

func (*HTMLEngine) Delims

func (v *HTMLEngine) Delims(left, right string)

Delims 设置视图动作的左右限定符

func (*HTMLEngine) Func

func (v *HTMLEngine) Func(funcMap template.FuncMap)

Func 设置视图函数

func (*HTMLEngine) Layout

func (v *HTMLEngine) Layout(layout string)

Layout 设置视图布局文件

func (*HTMLEngine) Load

func (v *HTMLEngine) Load() (err error)

Load 加载视图文件下的所有视图文件

func (*HTMLEngine) Render

func (v *HTMLEngine) Render(w io.Writer, name string, layout string, data interface{}, c *Context) error

Render 渲染视图

type HTTPError

type HTTPError interface {
	error
	Status() int
}

HTTPError HTTP 错误接口

func NewHTTPError

func NewHTTPError(status int, message ...string) HTTPError

NewHTTPError 创建 HttpError 实例

type HandlerFunc

type HandlerFunc func(*Context) error

HandlerFunc 用于处理请求的函数

func NotFoundHandler

func NotFoundHandler() HandlerFunc

NotFoundHandler 默认 NotFound 处理程序

type Map

type Map map[string]interface{}

Map 定义类型为 `map [string] interface {}` 的通用 map

type Response

type Response struct {
	Writer http.ResponseWriter
	// contains filtered or unexported fields
}

Response 包装一个 http.ResponseWriter 并实现其要使用的接口

func (*Response) Flush

func (res *Response) Flush()

Flush 刷新输出缓冲

func (*Response) Hijack

func (res *Response) Hijack() (net.Conn, *bufio.ReadWriter, error)

Hijack 实现 http.Hijacker 接口

func (*Response) Reset

func (res *Response) Reset(w http.ResponseWriter) bool

Reset 重置 Response

func (*Response) Size

func (res *Response) Size() int

Size 返回写入数据的大小

func (*Response) Status

func (res *Response) Status() int

Status 返回状态码

func (*Response) Write

func (res *Response) Write(b []byte) (int, error)

Write 向客户端写入回复的数据

func (*Response) WriteHeader

func (res *Response) WriteHeader(code int)

WriteHeader 发送 HTTP status code

func (*Response) Written

func (res *Response) Written() bool

Written 是否已经向客户端写入数据

type Route

type Route struct {
	// contains filtered or unexported fields
}

Route 包含注册路由的有关信息

func NewRoute

func NewRoute(method, path string, handlers []HandlerFunc) *Route

NewRoute 创建路由

func (*Route) Name

func (r *Route) Name(name string)

Name 命名路由

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router 路由器

func (*Router) Any

func (r *Router) Any(relativePath string, handlers ...HandlerFunc) []*Route

Any 注册实现响应所有 HTTP 请求的路由

func (*Router) DELETE

func (r *Router) DELETE(relativePath string, handlers ...HandlerFunc) *Route

DELETE 注册一个 HTTP DELETE 方法的路由

func (*Router) File

func (r *Router) File(relativePath, filepath string)

File 输出指定的文件

func (*Router) GET

func (r *Router) GET(relativePath string, handlers ...HandlerFunc) *Route

GET 注册一个 HTTP GET 方法的路由

func (*Router) Group

func (r *Router) Group(prefix string) *Router

Group 创建指定路径前缀的路由分组

func (*Router) HEAD

func (r *Router) HEAD(relativePath string, handlers ...HandlerFunc) *Route

HEAD 注册一个 HTTP HEAD 方法的路由

func (*Router) Layout

func (r *Router) Layout(layoutFile string)

Layout 设置路由分组的视图布局

func (*Router) Match

func (r *Router) Match(methods string, relativePath string, handlers ...HandlerFunc) []*Route

Match 注册指定响应 HTTP 请求的路由

func (*Router) NoLayout

func (r *Router) NoLayout()

NoLayout 设置路由分组不使用视图布局

func (*Router) OPTIONS

func (r *Router) OPTIONS(relativePath string, handlers ...HandlerFunc) *Route

OPTIONS 注册一个 HTTP OPTIONS 方法的路由

func (*Router) PATCH

func (r *Router) PATCH(relativePath string, handlers ...HandlerFunc) *Route

PATCH 注册一个 HTTP PATCH 方法的路由

func (*Router) POST

func (r *Router) POST(relativePath string, handlers ...HandlerFunc) *Route

POST 注册一个 HTTP POST 方法的路由

func (*Router) PUT

func (r *Router) PUT(relativePath string, handlers ...HandlerFunc) *Route

PUT 注册一个 HTTP PUT 方法的路由

func (*Router) Static

func (r *Router) Static(relativePath, root string)

Static 静态文件

router.Static("/static", "/public") router.Static("/static", "./public/static")

func (*Router) Use

func (r *Router) Use(middleware ...HandlerFunc)

Use 添加中间件到当前的 Router

type UploadedFile

type UploadedFile struct {
	File *multipart.FileHeader
	Name string
}

UploadedFile 单个上传的文件

func (*UploadedFile) Store

func (f *UploadedFile) Store(path string) (int64, error)

Store 把上传文件移动到指定的目录

func (*UploadedFile) StoreAs

func (f *UploadedFile) StoreAs(path, filename string) (int64, error)

StoreAs 将表单文件上传到指定的目录,并指定文件名

type UploadedFiles

type UploadedFiles struct {
	Form  *multipart.Form
	Files []*UploadedFile
	Name  string
}

UploadedFiles 多个上传的文件

func (*UploadedFiles) Store

func (f *UploadedFiles) Store(path string) (written int64, err error)

Store 将多个上传文件上传到指定的目录

type ViewEngine

type ViewEngine interface {
	Load() error
	Render(io.Writer, string, string, interface{}, *Context) error
}

ViewEngine 视图引擎接口

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL