学习网站来源:Gee
项目目录结构:
module example
go 1.21.5
require gee v0.0.0
replace gee => ./gee
package main
import (
"gee"
"net/http"
)
func main() {
r := gee.New()
//http.StatusOK为常量200
c.HTML(http.StatusOK, "<h1>Hello Gee</h1>")
})
r.GET("/hello", func(c *gee.Context) {
c.String(http.StatusOK, "hello %s, you're at %s\n", c.Query("name"), c.Path)
})
r.POST("/login", func(c *gee.Context) {
c.JSON(http.StatusOK, gee.H{
"username": c.PostForm("username"),
"password": c.PostForm("password"),
})
})
r.Run(":9999")
}
Handler的参数变成成了gee.Context,提供了查询Query/PostForm参数的功能
/hello?firstname=Jane&lastname=Doe这样一个路由, firstname, lastname即是Querystring parameters, 要获取他们就需要使用Query相关函数.
c.Query("firstname") // Jane
c.Query("lastname") // Doe
对于POST, PUT等这些能够传递参数Body的请求, 要获取其参数, 需要使用PostForm
{
"username":wang,
"password":123
}
username:= c.PostForm("username")
password:= c.PostForm("password")
效果如下:
对于post请求
package gee
import (
"encoding/json"
"fmt"
"net/http"
)
type H map[string]interface{}
//自定义的Context结构体是一个用于在处理 HTTP 请求时封装请求上下文信息的工具,它简化了处理请求和返回响应的过程,提供了更加友好和便利的接口
type Context struct {
Writer http.ResponseWriter
Req *http.Request
Path string
Method string
StatusCode int
}
func newContext(w http.ResponseWriter, req *http.Request) *Context {
return &Context{
Writer: w,
Req: req,
Path: req.URL.Path,
Method: req.Method,
}
}
func (c *Context) PostForm(key string) string {
return c.Req.FormValue(key)
}
func (c *Context) Query(key string) string {
return c.Req.URL.Query().Get(key)
}
//用于设置响应的状态码
func (c *Context) Status(code int) {
c.StatusCode = code
c.Writer.WriteHeader(code)
}
//用于设置响应头中指定键的值
func (c *Context) SetHeader(key string, value string) {
c.Writer.Header().Set(key, value)
}
//用于返回一个字符串格式的响应,设置响应头的 Content-Type 为 text/plain,并将格式化后的字符串写入到响应体中
func (c *Context) String(code int, format string, values ...interface{}) {
c.SetHeader("Context-Type", "text/plain")
c.Status(code)
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}
//使用json.NewEncoder将对象编码为JSON格式并写入到响应体中,如果在编码过程中出现错误,会返回 500 状态码
func (c *Context) JSON(code int, obj interface{}) {
c.SetHeader("Context-Type", "application/json")
c.Status(code)
encoder := json.NewEncoder(c.Writer)
if err := encoder.Encode(obj); err != nil {
http.Error(c.Writer, err.Error(), 500)
}
}
//用于返回原始的数据
func (c *Context) Data(code int, data []byte) {
c.Status(code)
c.Writer.Write(data)
}
//用于返回 HTML 内容,它接受一个状态码和一个 HTML 字符串作为参数
func (c *Context) HTML(code int, html string) {
c.SetHeader("Content-Type", "text/html")
c.Status(code)
c.Writer.Write([]byte(html))
}
关于HTTP中的Context-Type
常见的媒体格式类型如下:
以application开头的媒体格式类型:
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
将和路由相关的方法和结构单独提取了出来,放到了一个新的文件中router.go,方便我们下一次对 router 的功能进行增强,例如提供动态路由的支持
package gee
import (
"log"
"net/http"
)
type router struct {
handlers map[string]HandlerFunc
}
func newRouter() *router {
return &router{
handlers: make(map[string]HandlerFunc),
}
}
func (r *router) addRoute(method string, pattern string, handler HandlerFunc) {
log.Printf("Route %4s - %s", method, pattern)
key := method + "-" + pattern
r.handlers[key] = handler
}
func (r *router) handle(c *Context) {
key := c.Method + "-" + c.Path
if handler, ok := r.handlers[key]; ok {
handler(c)
} else {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
}
}
框架入口
package gee
import "net/http"
type HandlerFunc func(*Context)
type Engine struct {
router *router
}
func New() *Engine {
return &Engine{router: newRouter()}
}
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
engine.router.addRoute(method, pattern, handler)
}
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
engine.addRoute("GET", pattern, handler)
}
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
engine.addRoute("POST", pattern, handler)
}
func (engine *Engine) Run(addr string) (err error) {
return http.ListenAndServe(":9999", engine)
}
func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := newContext(w, r)
engine.router.handle(c)
}