From 2a2cfc289c96a020c1d7e96d575535fa23ef45a8 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 1 Dec 2019 14:07:36 +0800 Subject: [PATCH] improve ghttp.Client --- .example/net/ghttp/server/form/form-client.go | 9 ++ .example/net/ghttp/server/form/form.go | 2 +- net/ghttp/ghttp_client_config.go | 137 +++++++++++------- net/ghttp/ghttp_client_request.go | 51 +------ net/ghttp/ghttp_request.go | 4 +- net/ghttp/ghttp_request_param.go | 5 + net/ghttp/ghttp_request_param_custom.go | 10 +- net/ghttp/ghttp_request_param_form.go | 4 +- net/ghttp/ghttp_request_param_post.go | 60 ++++---- net/ghttp/ghttp_request_param_query.go | 20 ++- net/ghttp/ghttp_request_param_request.go | 94 +++++++++--- net/ghttp/ghttp_server_config.go | 110 +++++++------- net/ghttp/ghttp_server_config_route.go | 12 -- net/ghttp/ghttp_unit_param_test.go | 41 ++++++ net/ghttp/ghttp_unit_router_basic_test.go | 1 - 15 files changed, 335 insertions(+), 225 deletions(-) create mode 100644 .example/net/ghttp/server/form/form-client.go diff --git a/.example/net/ghttp/server/form/form-client.go b/.example/net/ghttp/server/form/form-client.go new file mode 100644 index 000000000..0ae72051c --- /dev/null +++ b/.example/net/ghttp/server/form/form-client.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/gogf/gf/net/ghttp" +) + +func main() { + ghttp.PostContent("http://127.0.0.1:8199/", "array[]=1&array[]=2") +} diff --git a/.example/net/ghttp/server/form/form.go b/.example/net/ghttp/server/form/form.go index 6731f64c2..b12b99c75 100644 --- a/.example/net/ghttp/server/form/form.go +++ b/.example/net/ghttp/server/form/form.go @@ -8,7 +8,7 @@ import ( func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request) { - g.Dump(r.GetPostMap()) + g.Dump(r.GetForm("array")) r.Response.WriteTpl("form.html") }) s.SetPort(8199) diff --git a/net/ghttp/ghttp_client_config.go b/net/ghttp/ghttp_client_config.go index 08baaf8aa..3f06ff57f 100644 --- a/net/ghttp/ghttp_client_config.go +++ b/net/ghttp/ghttp_client_config.go @@ -4,99 +4,136 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// HTTP客户端请求. - package ghttp import ( + "crypto/tls" + "net/http" "strings" "time" "github.com/gogf/gf/text/gregex" ) -// 是否模拟浏览器模式(自动保存提交COOKIE) -func (c *Client) SetBrowserMode(enabled bool) { - c.browserMode = enabled +// Client is the HTTP client for HTTP request management. +type Client struct { + http.Client // Underlying HTTP Client. + header map[string]string // Custom header map. + cookies map[string]string // Custom cookie map. + prefix string // Prefix for request. + authUser string // HTTP basic authentication: user. + authPass string // HTTP basic authentication: pass. + browserMode bool // Whether auto saving and sending cookie content. + retryCount int // Retry count when request fails. + retryInterval int // Retry interval when request fails. } -// 设置HTTP Header -func (c *Client) SetHeader(key, value string) { - c.header[key] = value -} - -func (c *Client) SetHeaderMap(m map[string]string) { - for k, v := range m { - c.header[k] = v +// NewClient creates and returns a new HTTP client object. +func NewClient() *Client { + return &Client{ + Client: http.Client{ + Transport: &http.Transport{ + // No validation for https certification of the server. + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + DisableKeepAlives: true, + }, + }, + header: make(map[string]string), + cookies: make(map[string]string), } } -// 通过字符串设置HTTP Header -func (c *Client) SetHeaderRaw(header string) { +// Clone clones current client and returns a new one. +func (c *Client) Clone() *Client { + newClient := NewClient() + *newClient = *c + newClient.header = make(map[string]string) + newClient.cookies = make(map[string]string) + for k, v := range c.header { + newClient.header[k] = v + } + for k, v := range c.cookies { + newClient.cookies[k] = v + } + return newClient +} + +// SetBrowserMode enables browser mode of the client. +// When browser mode is enabled, it automatically saves and sends cookie content +// from and to server. +func (c *Client) SetBrowserMode(enabled bool) *Client { + c.browserMode = enabled + return c +} + +// SetHeader sets a custom HTTP header pair for the client. +func (c *Client) SetHeader(key, value string) *Client { + c.header[key] = value + return c +} + +// SetHeaderMap sets custom HTTP headers with map. +func (c *Client) SetHeaderMap(m map[string]string) *Client { + for k, v := range m { + c.header[k] = v + } + return c +} + +// SetContentType sets HTTP content type for the client. +func (c *Client) SetContentType(contentType string) *Client { + c.header["Content-Type"] = contentType + return c +} + +// SetHeaderRaw sets custom HTTP header using raw string. +func (c *Client) SetHeaderRaw(header string) *Client { for _, line := range strings.Split(strings.TrimSpace(header), "\n") { array, _ := gregex.MatchString(`^([\w\-]+):\s*(.+)`, line) if len(array) >= 3 { c.header[array[1]] = array[2] } } + return c } -// 设置COOKIE -func (c *Client) SetCookie(key, value string) { +// SetCookie sets a cookie pair for the client. +func (c *Client) SetCookie(key, value string) *Client { c.cookies[key] = value + return c } -// 使用Map设置COOKIE -func (c *Client) SetCookieMap(m map[string]string) { +// SetCookieMap sets cookie items with map. +func (c *Client) SetCookieMap(m map[string]string) *Client { for k, v := range m { c.cookies[k] = v } + return c } -// 设置请求的URL前缀 -func (c *Client) SetPrefix(prefix string) { +// SetPrefix sets the request server URL prefix. +func (c *Client) SetPrefix(prefix string) *Client { c.prefix = prefix -} - -// 设置请求过期时间 -func (c *Client) SetTimeOut(t time.Duration) { - c.Timeout = t -} - -// 设置HTTP访问账号密码 -func (c *Client) SetBasicAuth(user, pass string) { - c.authUser = user - c.authPass = pass -} - -// 设置失败重试次数及间隔,失败仅针对网络请求失败情况。 -// 重试间隔时间单位为秒。 -func (c *Client) SetRetry(retryCount int, retryInterval int) { - c.retryCount = retryCount - c.retryInterval = retryInterval -} - -// 链式操作, See SetBrowserMode -func (c *Client) BrowserMode(enabled bool) *Client { - c.browserMode = enabled return c } -// 链式操作, See SetTimeOut -func (c *Client) TimeOut(t time.Duration) *Client { +// SetTimeOut sets the request timeout for the client. +func (c *Client) SetTimeOut(t time.Duration) *Client { c.Timeout = t return c } -// 链式操作, See SetBasicAuth -func (c *Client) BasicAuth(user, pass string) *Client { +// SetBasicAuth sets HTTP basic authentication information for the client. +func (c *Client) SetBasicAuth(user, pass string) *Client { c.authUser = user c.authPass = pass return c } -// 链式操作, See SetRetry -func (c *Client) Retry(retryCount int, retryInterval int) *Client { +// SetRetry sets retry count and interval. +func (c *Client) SetRetry(retryCount int, retryInterval int) *Client { c.retryCount = retryCount c.retryInterval = retryInterval return c diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 4e5a93158..06acb3631 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -8,7 +8,6 @@ package ghttp import ( "bytes" - "crypto/tls" "encoding/json" "errors" "fmt" @@ -22,51 +21,6 @@ import ( "github.com/gogf/gf/os/gfile" ) -// Client is the HTTP client for HTTP request management. -type Client struct { - http.Client // Underlying HTTP Client. - header map[string]string // Custom header map. - cookies map[string]string // Custom cookie map. - prefix string // Prefix for request. - authUser string // HTTP basic authentication: user. - authPass string // HTTP basic authentication: pass. - browserMode bool // Whether auto saving and sending cookie content. - retryCount int // Retry count when request fails. - retryInterval int // Retry interval when request fails. -} - -// NewClient creates and returns a new HTTP client object. -func NewClient() *Client { - return &Client{ - Client: http.Client{ - Transport: &http.Transport{ - // No validation for https certification of the server. - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - DisableKeepAlives: true, - }, - }, - header: make(map[string]string), - cookies: make(map[string]string), - } -} - -// Clone clones current client and returns a new one. -func (c *Client) Clone() *Client { - newClient := NewClient() - *newClient = *c - newClient.header = make(map[string]string) - newClient.cookies = make(map[string]string) - for k, v := range c.header { - newClient.header[k] = v - } - for k, v := range c.cookies { - newClient.cookies[k] = v - } - return newClient -} - // Get send GET request and returns the response object. // Note that the response object MUST be closed if it'll be never used. func (c *Client) Get(url string) (*ClientResponse, error) { @@ -259,6 +213,11 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (*ClientResp req.Header.Set(k, v) } } + // Automatically set default content type to "application/x-www-form-urlencoded" + // if there' no content type set. + if _, ok := c.header["Content-Type"]; !ok { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } // Custom cookie. if len(c.cookies) > 0 { headerCookie := "" diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index dfd55c2ea..855fdc74f 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -36,13 +36,13 @@ type Request struct { parsedQuery bool // A bool marking whether the GET parameters parsed. parsedBody bool // A bool marking whether the request body parsed. parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH. + paramsMap map[string]interface{} // Custom parameters. routerMap map[string]interface{} // Router parameters map, which might be nil if there're no router parameters. queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string. - formMap map[string]interface{} // Form parameters map, which is nil if there's no form. + formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client. bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content. error error // Current executing error of the request. exit bool // A bool marking whether current request is exited. - params map[string]interface{} // Custom parameters. parsedHost string // The parsed host name for current host used by GetHost function. clientIp string // The parsed client ip for current host used by GetClientIp function. bodyContent []byte // Request body content. diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 119f5de4c..656bd0c8d 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -209,6 +209,11 @@ func (r *Request) ParseForm() { if r.formMap, err = gstr.Parse(params); err != nil { panic(err) } + } else { + r.ParseBody() + if len(r.bodyMap) > 0 { + r.formMap = r.bodyMap + } } } } diff --git a/net/ghttp/ghttp_request_param_custom.go b/net/ghttp/ghttp_request_param_custom.go index 26303a65c..cd788471f 100644 --- a/net/ghttp/ghttp_request_param_custom.go +++ b/net/ghttp/ghttp_request_param_custom.go @@ -10,18 +10,18 @@ import "github.com/gogf/gf/container/gvar" // SetParam sets custom parameter with key-value pair. func (r *Request) SetParam(key string, value interface{}) { - if r.params == nil { - r.params = make(map[string]interface{}) + if r.paramsMap == nil { + r.paramsMap = make(map[string]interface{}) } - r.params[key] = value + r.paramsMap[key] = value } // GetParam returns custom parameter with given name . // It returns if does not exist. // It returns nil if is not passed. func (r *Request) GetParam(key string, def ...interface{}) interface{} { - if r.params != nil { - return r.params[key] + if r.paramsMap != nil { + return r.paramsMap[key] } if len(def) > 0 { return def[0] diff --git a/net/ghttp/ghttp_request_param_form.go b/net/ghttp/ghttp_request_param_form.go index 76416cbff..3c450a3af 100644 --- a/net/ghttp/ghttp_request_param_form.go +++ b/net/ghttp/ghttp_request_param_form.go @@ -106,11 +106,11 @@ func (r *Request) GetFormInterfaces(key string, def ...interface{}) []interface{ // the associated values are the default values if the client does not pass. func (r *Request) GetFormMap(kvMap ...map[string]interface{}) map[string]interface{} { r.ParseForm() - if len(kvMap) > 0 { + if len(kvMap) > 0 && kvMap[0] != nil { if len(r.formMap) == 0 { return kvMap[0] } - m := make(map[string]interface{}) + m := make(map[string]interface{}, len(kvMap[0])) for k, defValue := range kvMap[0] { if postValue, ok := r.formMap[k]; ok { m[k] = postValue diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go index f292f9303..92f8ce2a3 100644 --- a/net/ghttp/ghttp_request_param_post.go +++ b/net/ghttp/ghttp_request_param_post.go @@ -15,16 +15,19 @@ import ( // GetPost retrieves and returns parameter from form and body. // It returns if does not exist in neither form nor body. // It returns nil if is not passed. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: form < body. func (r *Request) GetPost(key string, def ...interface{}) interface{} { r.ParseForm() r.ParseBody() - if len(r.formMap) > 0 { - if v, ok := r.formMap[key]; ok { + if len(r.bodyMap) > 0 { + if v, ok := r.bodyMap[key]; ok { return v } } - if len(r.bodyMap) > 0 { - if v, ok := r.bodyMap[key]; ok { + if len(r.formMap) > 0 { + if v, ok := r.formMap[key]; ok { return v } } @@ -101,38 +104,39 @@ func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{ // GetPostMap retrieves and returns all parameters in the form and body passed from client // as map. The parameter specifies the keys retrieving from client parameters, // the associated values are the default values if the client does not pass. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: form < body. func (r *Request) GetPostMap(kvMap ...map[string]interface{}) map[string]interface{} { r.ParseForm() r.ParseBody() + var ok, filter bool + if len(kvMap) > 0 && kvMap[0] != nil { + filter = true + } m := make(map[string]interface{}, len(r.formMap)+len(r.bodyMap)) - if len(kvMap) > 0 { - if len(r.formMap) == 0 && len(r.bodyMap) == 0 { - return kvMap[0] - } - if len(r.formMap) > 0 { - for k, v := range kvMap[0] { - if postValue, ok := r.formMap[k]; ok { - m[k] = postValue - } else { - m[k] = v - } + for k, v := range r.formMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue } } - if len(r.bodyMap) > 0 { - for k, v := range kvMap[0] { - if postValue, ok := r.bodyMap[k]; ok { - m[k] = postValue - } else { - m[k] = v - } + m[k] = v + } + for k, v := range r.bodyMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue } } - } else { - for k, v := range r.formMap { - m[k] = v - } - for k, v := range r.bodyMap { - m[k] = v + m[k] = v + } + // Check none exist parameters and assign it with default value. + if filter { + for k, v := range kvMap[0] { + if _, ok = m[k]; !ok { + m[k] = v + } } } return m diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index 8fa51b2ae..813531664 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -25,16 +25,19 @@ func (r *Request) SetQuery(key string, value interface{}) { // GetQuery retrieves and returns parameter with given name from query string // and request body. It returns if does not exist in the query. It returns nil // if is not passed. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: query < body. func (r *Request) GetQuery(key string, def ...interface{}) interface{} { r.ParseQuery() r.ParseBody() - if len(r.queryMap) > 0 { - if v, ok := r.queryMap[key]; ok { + if len(r.bodyMap) > 0 { + if v, ok := r.bodyMap[key]; ok { return v } } - if len(r.bodyMap) > 0 { - if v, ok := r.bodyMap[key]; ok { + if len(r.queryMap) > 0 { + if v, ok := r.queryMap[key]; ok { return v } } @@ -111,14 +114,18 @@ func (r *Request) GetQueryInterfaces(key string, def ...interface{}) []interface // GetQueryMap retrieves and returns all parameters passed from client using HTTP GET method // as map. The parameter specifies the keys retrieving from client parameters, // the associated values are the default values if the client does not pass. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: query < body. func (r *Request) GetQueryMap(kvMap ...map[string]interface{}) map[string]interface{} { r.ParseQuery() r.ParseBody() - m := make(map[string]interface{}, len(r.queryMap)+len(r.bodyMap)) - if len(kvMap) > 0 { + var m map[string]interface{} + if len(kvMap) > 0 && kvMap[0] != nil { if len(r.queryMap) == 0 && len(r.bodyMap) == 0 { return kvMap[0] } + m = make(map[string]interface{}, len(kvMap[0])) if len(r.queryMap) > 0 { for k, v := range kvMap[0] { if postValue, ok := r.queryMap[k]; ok { @@ -138,6 +145,7 @@ func (r *Request) GetQueryMap(kvMap ...map[string]interface{}) map[string]interf } } } else { + m = make(map[string]interface{}, len(r.queryMap)+len(r.bodyMap)) for k, v := range r.queryMap { m[k] = v } diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index f4483611f..472edb56e 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -12,32 +12,38 @@ import ( "github.com/gogf/gf/util/gconv" ) -// GetRequestVar retrieves and returns the parameter named passed from client as interface{}, +// GetRequest retrieves and returns the parameter named passed from client as interface{}, // no matter what HTTP method the client is using. The parameter specifies the default value // if the does not exist. // -// Note that the parameter is retrieved in order of: router->get/body->post/body->param. +// GetRequest is one of the most commonly used functions for retrieving parameters. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: router < query < form < body < custom. func (r *Request) GetRequest(key string, def ...interface{}) interface{} { - v := r.GetRouterValue(key) - if v == nil { - r.ParseQuery() - if len(r.queryMap) > 0 { - v, _ = r.queryMap[key] + value := r.GetParam(key) + if value == nil { + r.ParseBody() + if len(r.bodyMap) > 0 { + value = r.bodyMap[key] } } - if v == nil { - v = r.GetPost(key) + if value == nil { + value = r.GetForm(key) } - if v == nil { - v = r.GetParam(key) + if value == nil { + value = r.GetQuery(key) } - if v != nil { - return v + if value == nil { + value = r.GetRouterValue(key) + } + if value != nil { + return value } if len(def) > 0 { return def[0] } - return v + return value } // GetRequestVar retrieves and returns the parameter named passed from client as *gvar.Var, @@ -155,26 +161,70 @@ func (r *Request) GetRequestInterfaces(key string, def ...interface{}) []interfa // GetRequestMap retrieves and returns all parameters passed from client as map, // no matter what HTTP method the client is using. The parameter specifies the keys // retrieving from client parameters, the associated values are the default values if the client -// does not pass. +// does not pass the according keys. +// +// GetRequestMap is one of the most commonly used functions for retrieving parameters. +// +// Note that if there're multiple parameters with the same name, the parameters are retrieved and overwrote +// in order of priority: router < query < form < body < custom. func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]interface{} { r.ParseQuery() r.ParseForm() r.ParseBody() - m := make(map[string]interface{}, len(r.queryMap)+len(r.formMap)+len(r.bodyMap)) + var ok, filter bool + var length int + if len(kvMap) > 0 && kvMap[0] != nil { + length = len(kvMap[0]) + filter = true + } else { + length = len(r.routerMap) + len(r.queryMap) + len(r.formMap) + len(r.bodyMap) + len(r.paramsMap) + } + m := make(map[string]interface{}, length) + for k, v := range r.routerMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue + } + } + m[k] = v + } for k, v := range r.queryMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue + } + } m[k] = v } for k, v := range r.formMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue + } + } m[k] = v } for k, v := range r.bodyMap { + if filter { + if _, ok = kvMap[0][k]; !ok { + continue + } + } m[k] = v } - if len(kvMap) > 0 && kvMap[0] != nil { - var ok bool - for k, _ := range m { + for k, v := range r.paramsMap { + if filter { if _, ok = kvMap[0][k]; !ok { - delete(m, k) + continue + } + } + m[k] = v + } + // Check none exist parameters and assign it with default value. + if filter { + for k, v := range kvMap[0] { + if _, ok = m[k]; !ok { + m[k] = v } } } @@ -188,7 +238,7 @@ func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]inte func (r *Request) GetRequestMapStrStr(kvMap ...map[string]interface{}) map[string]string { requestMap := r.GetRequestMap(kvMap...) if len(requestMap) > 0 { - m := make(map[string]string) + m := make(map[string]string, len(requestMap)) for k, v := range requestMap { m[k] = gconv.String(v) } @@ -204,7 +254,7 @@ func (r *Request) GetRequestMapStrStr(kvMap ...map[string]interface{}) map[strin func (r *Request) GetRequestMapStrVar(kvMap ...map[string]interface{}) map[string]*gvar.Var { requestMap := r.GetRequestMap(kvMap...) if len(requestMap) > 0 { - m := make(map[string]*gvar.Var) + m := make(map[string]*gvar.Var, len(requestMap)) for k, v := range requestMap { m[k] = gvar.New(v) } diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 637163fea..2c508cebe 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -26,62 +26,72 @@ import ( const ( gDEFAULT_HTTP_ADDR = ":80" // Default listening port for HTTP. gDEFAULT_HTTPS_ADDR = ":443" // Default listening port for HTTPS. - URI_TYPE_DEFAULT = 0 // Type for method name to URI converting, which converts name to its lower case and joins the words using char '-'. - URI_TYPE_FULLNAME = 1 // Type for method name to URI converting, which does no converting to the method name. - URI_TYPE_ALLLOWER = 2 // Type for method name to URI converting, which converts name to its lower case. - URI_TYPE_CAMEL = 3 // Type for method name to URI converting, which converts name to its camel case. + URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'. + URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name. + URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case. + URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case. ) // HTTP Server configuration. type ServerConfig struct { - Address string // Server listening address like ":port", multiple addresses joining using ',' - HTTPSAddr string // HTTPS addresses, multiple addresses joining using char ','. - HTTPSCertPath string // HTTPS certification file path. - HTTPSKeyPath string // HTTPS key file path. - Handler http.Handler // Default request handler function. - ReadTimeout time.Duration // Maximum duration for reading the entire request, including the body. - WriteTimeout time.Duration // Maximum duration before timing out writes of the response. - IdleTimeout time.Duration // Maximum amount of time to wait for the next request when keep-alives are enabled. - MaxHeaderBytes int // Maximum number of bytes the server will read parsing the request header's keys and values, including the request line. - TLSConfig *tls.Config // TLS configuration for use by ServeTLS and ListenAndServeTLS. - KeepAlive bool // HTTP keep-alive are enabled or not. - ServerAgent string // Server Agent. - View *gview.View // View engine for the server. - Rewrites map[string]string // URI rewrite rules. - IndexFiles []string // Static: 默认访问的文件列表 - IndexFolder bool // Static: 如果访问目录是否显示目录列表 - ServerRoot string // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低) - SearchPaths []string // Static: 静态文件搜索目录(包含ServerRoot,按照优先级进行排序) - StaticPaths []staticPathItem // Static: 静态文件目录映射(按照优先级进行排序) - FileServerEnabled bool // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别) - CookieMaxAge time.Duration // Cookie: 有效期 - CookiePath string // Cookie: 有效Path(注意同时也会影响SessionID) - CookieDomain string // Cookie: 有效Domain(注意同时也会影响SessionID) - SessionMaxAge time.Duration // Session: 有效期 - SessionIdName string // Session: SessionId. - SessionPath string // Session: Session Storage path for storing session files. - SessionStorage gsession.Storage // Session: Session Storage implementer. - DenyIps []string // Security: 不允许访问的ip列表,支持ip前缀过滤,如: 10 将不允许10开头的ip访问 - AllowIps []string // Security: 仅允许访问的ip列表,支持ip前缀过滤,如: 10 将仅允许10开头的ip访问 - DenyRoutes []string // Security: 不允许访问的路由规则列表 - Logger *glog.Logger // Logging: Custom logger for server. - LogPath string // Logging: 存放日志的目录路径(默认为空,表示不写文件) - LogStdout bool // Logging: 是否打印日志到终端(默认开启) - ErrorStack bool // Logging: 当产生错误时打印调用链详细堆栈 - ErrorLogEnabled bool // Logging: 是否开启error log(默认开启) - ErrorLogPattern string // Logging: Error log file pattern like: error-{Ymd}.log - AccessLogEnabled bool // Logging: 是否开启access log(默认关闭) - AccessLogPattern string // Logging: Error log file pattern like: access-{Ymd}.log - PProfEnabled bool // PProf: Enable PProf feature or not. - PProfPattern string // PProf: PProf pattern for router, it enables PProf feature if it's not empty. - FormParsingMemory int64 // Mess: 表单解析内存限制(byte) - NameToUriType int // Mess: 服务注册时对象和方法名称转换为URI时的规则 - GzipContentTypes []string // Mess: 允许进行gzip压缩的文件类型 - DumpRouteMap bool // Mess: 是否在程序启动时默认打印路由表信息 - RouterCacheExpire int // Mess: 路由检索缓存过期时间(秒) + // Basic + Address string // Server listening address like ":port", multiple addresses joined using ','. + HTTPSAddr string // HTTPS addresses, multiple addresses joined using char ','. + HTTPSCertPath string // HTTPS certification file path. + HTTPSKeyPath string // HTTPS key file path. + TLSConfig *tls.Config // TLS configuration for use by ServeTLS and ListenAndServeTLS. + Handler http.Handler // Request handler. + ReadTimeout time.Duration // Maximum duration for reading the entire request, including the body. + WriteTimeout time.Duration // Maximum duration before timing out writes of the response. + IdleTimeout time.Duration // Maximum amount of time to wait for the next request when keep-alive is enabled. + MaxHeaderBytes int // Maximum number of bytes the server will read parsing the request header's keys and values, including the request line. + KeepAlive bool // Enable HTTP keep-alive. + ServerAgent string // Server Agent. + View *gview.View // View object for the server. + + // Static + Rewrites map[string]string // URI rewrite rules map. + IndexFiles []string // The index files for static folder. + IndexFolder bool // Whether list sub-files when requesting folder; server responses HTTP status code 403 if it's false. + ServerRoot string // The root directory for static service. + SearchPaths []string // Additional searching directories for static service. + StaticPaths []staticPathItem // URI to directory mapping array. + FileServerEnabled bool // Switch for static service. + + // Cookie + CookieMaxAge time.Duration // Max TTL for cookie items. + CookiePath string // Cookie Path(also affects the default storage for session id). + CookieDomain string // Cookie Domain(also affects the default storage for session id). + + // Session + SessionMaxAge time.Duration // Max TTL for session items. + SessionIdName string // Session id name. + SessionPath string // Session Storage directory path for storing session files. + SessionStorage gsession.Storage // Session Storage implementer. + + // Logging + Logger *glog.Logger // Logger for server. + LogPath string // Directory for storing logging files. + LogStdout bool // Printing logging content to stdout. + ErrorStack bool // Logging stack information when error. + ErrorLogEnabled bool // Enable error logging files. + ErrorLogPattern string // Error log file pattern like: error-{Ymd}.log + AccessLogEnabled bool // Enable access logging files. + AccessLogPattern string // Error log file pattern like: access-{Ymd}.log + + // PProf + PProfEnabled bool // Enable PProf feature. + PProfPattern string // PProf service pattern for router, it automatically enables PProf feature if called. + + // Other + FormParsingMemory int64 // 表单解析内存限制(byte) + NameToUriType int // 服务注册时对象和方法名称转换为URI时的规则 + GzipContentTypes []string // 允许进行gzip压缩的文件类型 + DumpRouteMap bool // 是否在程序启动时默认打印路由表信息 + RouterCacheExpire int // 路由检索缓存过期时间(秒) } -// 默认HTTP Server配置 +// defaultServerConfig is the default configuration object for server. var defaultServerConfig = ServerConfig{ Address: "", HTTPSAddr: "", diff --git a/net/ghttp/ghttp_server_config_route.go b/net/ghttp/ghttp_server_config_route.go index 2762b8923..4cc81fb96 100644 --- a/net/ghttp/ghttp_server_config_route.go +++ b/net/ghttp/ghttp_server_config_route.go @@ -6,18 +6,6 @@ package ghttp -func (s *Server) SetDenyIps(ips []string) { - s.config.DenyIps = ips -} - -func (s *Server) SetAllowIps(ips []string) { - s.config.AllowIps = ips -} - -func (s *Server) SetDenyRoutes(routes []string) { - s.config.DenyRoutes = routes -} - // 设置URI重写规则 func (s *Server) SetRewrite(uri string, rewrite string) { s.config.Rewrites[uri] = rewrite diff --git a/net/ghttp/ghttp_unit_param_test.go b/net/ghttp/ghttp_unit_param_test.go index 488f263f2..3c12abb39 100644 --- a/net/ghttp/ghttp_unit_param_test.go +++ b/net/ghttp/ghttp_unit_param_test.go @@ -403,3 +403,44 @@ func Test_Params_Basic(t *testing.T) { gtest.Assert(client.PostContent("/struct-with-base", `id=1&name=john&password1=123&password2=456`), "1john1234561john123456") }) } + +func Test_Params_Priority(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.BindHandler("/query", func(r *ghttp.Request) { + r.Response.Write(r.GetQuery("a")) + }) + s.BindHandler("/post", func(r *ghttp.Request) { + r.Response.Write(r.GetPost("a")) + }) + s.BindHandler("/form", func(r *ghttp.Request) { + r.Response.Write(r.GetForm("a")) + }) + s.BindHandler("/request", func(r *ghttp.Request) { + r.Response.Write(r.Get("a")) + }) + s.BindHandler("/request-map", func(r *ghttp.Request) { + r.Response.Write(r.GetMap(g.Map{ + "a": 1, + "b": 2, + })) + }) + s.SetPort(p) + s.SetDumpRouteMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := ghttp.NewClient() + client.SetPrefix(prefix) + + gtest.Assert(client.GetContent("/query?a=1", "a=100"), "100") + gtest.Assert(client.PostContent("/post?a=1", "a=100"), "100") + gtest.Assert(client.PostContent("/form?a=1", "a=100"), "100") + gtest.Assert(client.PutContent("/form?a=1", "a=100"), "100") + gtest.Assert(client.GetContent("/request?a=1", "a=100"), "100") + gtest.Assert(client.GetContent("/request-map?a=1&b=2&c=3", "a=100&b=200&c=300"), `{"a":"100","b":"200"}`) + }) +} diff --git a/net/ghttp/ghttp_unit_router_basic_test.go b/net/ghttp/ghttp_unit_router_basic_test.go index ab80041f5..4afca7aed 100644 --- a/net/ghttp/ghttp_unit_router_basic_test.go +++ b/net/ghttp/ghttp_unit_router_basic_test.go @@ -16,7 +16,6 @@ import ( "github.com/gogf/gf/test/gtest" ) -// 基本路由功能测试 func Test_Router_Basic(t *testing.T) { p := ports.PopRand() s := g.Server(p)