Gin 框架(http server)

使用方法

/users/name/users/:id"hello""hello/"gin.RedirectTrailingSlash = truev1 := router.Group("/v1")func(c *gin.Context)gin.Contextval := c.Query()val, ok := c.GetQuery()keyval := c.DefaultQuery(, )c.[Should]BindQuery(&)formjsonc.QueryArray()keyvalue?media=blog&media=wechatc.QueryMap()?ids[a]=123&ids[b]=456&ids[c]=789c.Param():"/user/:name"/user/john/user//user*"/user/:name/*action"/user/john//user/John/love/tom/user/john/sendc.Param("action")//love/tom/sendc.ShouldBindUri(&)uri:"" binding:"required"Content-Typetext/plainBindBindJSONBindXMLBindYAMLgo-playground/validator.v8binding:"required"ShouldBindShouldBindJSONShouldBindXMLShouldBindYAMLc.ShouldBindBodyWithc.PostForm()c.DefaultPostForm(, )c.FormFile()form, _ := c.MultipartForm(); files := form.File[]c.JSON(, gin.H{: })gin.Hmap[string]interface{}c.JSON(, )c.IndentedJSONAbortWithStatusJSON(, gin.H{: })c.Next()()r := gin.Default()r := gin.New()r.Use(())r.Group(,()… )r.GET(, ()…, )
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 请求前操作
		c.Next()
		// 请求后操作
	}
}
r.GET("/test", func(c *gin.Context) {
    c.Request.URL.Path = "/test2"
    r.HandleContext(c)
})
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    engine.handleHTTPRequest(c)

    engine.pool.Put(c)
}

func (c *Context) Copy() *Context {
    var cp = *c
    cp.writermem.ResponseWriter = nil
    cp.Writer = &cp.writermem
    cp.index = abortIndex
    cp.handlers = nil
    cp.Keys = map[string]interface{}{}
    for k, v := range c.Keys {
        cp.Keys[k] = v
    }
    return &cp
}
c.Setc.Get
// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
gin.DisableConsoleColor()

// 记录到文件。
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)

// 同时将日志写入文件和控制台
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
  • 自定义HTTP配置
router := gin.Default()

s := &http.Server{
	Addr:           ":8080",
	Handler:        router,
	ReadTimeout:    10 * time.Second,
	WriteTimeout:   10 * time.Second,
	MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()

源码阅读

在这里插入图片描述

参考

resty框架(http client)
import "github.com/go-resty/resty/v2"

var client *resty.Client

fun init() {
	client = resty.New().
		SetTimeout(1 * time.Minute).
		SetRetryCount(3).
		SetRetryWaitTime(5 * time.Second). // 超时后等待5秒才重试
		SetRetryMaxWaitTime(20 * time.Second).
		SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
			ctx := r.Request.Context()

			if err != nil {
				logs.CtxWarn(ctx, "query retry on error: %v", err)
				return true
			}
			if r.StatusCode() >= http.StatusInternalServerError {
				logs.CtxWarn(ctx, "query retry on status code: %d", r.StatusCode())
				return true
			}

			return false
		}).
		SetHeaders(map[string]string{
        "Content-Type": "application/json",
        "User-Agent": "My custom User Agent String",
      	}).
		SetProxy("http://proxyserver:8888").
		SetBasicAuth("myuser", "mypass").
		OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
			ctx := r.Request.Context()
			isGzip := r.Header().Get("Content-Encoding") == "gzip"

			logs.CtxInfoKvs(ctx, "url", r.Request.URL, "latency", r.Time(), "size", r.Size(), "isGzip", isGzip)
			return nil
		})
}

func main() {
	resp, err := client.R().
		SetPathParams(map[string]string{
			"id": id,
		}).
		SetQueryParams(map[string]string{
			"name": "jack",
			"gender": "male",
		}).
		Get(url)
	
	fmt.Println("Error      :", err)
	fmt.Println("Status Code:", resp.StatusCode())
	fmt.Println("Request URL:", resp.Request.URL)
	_ = json.Unmarshal(resp.Body(), &res)
	
	ti := resp.Request.TraceInfo()
	fmt.Println("DNSLookup    :", ti.DNSLookup)
	fmt.Println("ConnTime     :", ti.ConnTime)
	fmt.Println("TotalTime    :", ti.TotalTime)
}
标准库

server

在这里插入图片描述

handler

ServeHTTP(http.ResponseWriter, *http.Request)ServeHTTPhttp.ServeMuxhttp.ServeMux/article/123httprouterhttp.ServeMux///hello/hello/there//hello/hello//hello///
func(s * server)handleGreeting(format string)http.HandlerFunc { 
    return func(w http.ResponseWriter,r * http.Request){ 
        fmt.Fprintf(w,format,"World")
    } 
}

mux.HandleFunc("/greeting", s.handleGreeting("Hello %s"))

client

在这里插入图片描述

request

  • 添加URL参数
params := make(url.Values)
params.Add("key1", "value1")
params.Add("key2", "value2")

req, _ := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
req.URL.RawQuery = params.Encode()
fmt.Println(req.URL) // 输出:http://httpbin.org/get?key1=value1&key2=value2
  • 设置请求代理
proxyUrl, err := url.Parse("http://127.0.0.1:8087")
if err != nil {
    panic(err)
}
t := &http.Transport{
    Proxy:           http.ProxyURL(proxyUrl),
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{ // 自定义client替换defaultClient
    Transport: t,
    Timeout:   time.Duration(10 * time.Second),
}
// 使用
res, err := client.Get("https://google.com")

response

w.WriteHeader()w.Header().Set()

form

r.Formmap[string][]stringr.ParseForm()r.PostFormaplication/x-www-form-urlencodedmultipart/form-datar.Formmultipart/form-datar.ParseMultipartForm()r.MultipartFormr.FormFile()FromValue()PostFromValue()ParseFormParseMultipartForm

transport

  • Transport字段代表向网络服务发送 HTTP 请求,并从网络服务接收 HTTP 响应的操作过程
    • 该字段的方法RoundTrip应该实现单次 HTTP 交互需要的所有步骤
      在这里插入图片描述

在这里插入图片描述

标准库

conn

TODO

dialer

TODO

resolver

TODO