There is a demand to hold arbitrary data in the request scope, such as passing data between multiple handlers or storing parsed path variables by an HTTP router (e.g., #60227). This proposal suggests a general mechanism for storing arbitrary data in the Request struct.
Background
Currently, the recommended approach for associating arbitrary data with a Request is to use Request.WithContext or Request.Clone along with context.WithValue:
req = req.WithContext(context.WithValue(userCtxKey, user))
// or
req = req.Clone(context.WithValue(userCtxKey, user))
The problem with this approach is that both methods create a copy of the Request (either shallow or deep), resulting in a performance hit. Additionally, users need to be cautious to avoid using the old Request.
It is worth mentioning that the primary purpose of Request.WithContext and Request.Clone is to enable users to control the entire lifetime of a request and its response: obtaining a connection, sending the request, and reading the response headers and body. Storing arbitrary data doesn't look like a good fit.
Before the context package was introduced (i.e., before Go 1.7), storing arbitrary data in Request was basically not possible. So as a workaround, HTTP routers or web frameworks designed alternative Handler variants, like func(ResponseWriter, *Request, map[string]string), to pass their parsed path variables along with ResponseWriter and Request. Or simply abandon Handler and introduce a Context struct of their own. The result is added complexity.
The #60227 also suggested a general mechanism to store arbitrary data in Request:
package http
type Request struct {
...
Vars map[string]any
...
}
However, in my opinion, the usage of the exported map[string]any has the following problems:
The user is required to perform a nil check before using it, which can be cumbersome.
The presence of the exported map introduces the risk of data tampering by other packages.
The use of string keys can lead to collisions, especially as it is also designed to store parsed path variables.
Proposal
I propose to add an unexported map[any]any field named "vaules" to the Request struct. This field is used to hold arbitrary data within the request scope. Additionally, we also need add two exported methods to the Request struct: func(key any) (value any, ok bool) as the getter and func(key, value any) as the setter for Request.values.
Implementation
This proposal will introduce the following API changes to the net/http package:
package http
type Request struct {
...
values map[any]any
...
}
// Value returns the value set in the r for the given key, or nil if no value is
// present. The ok result indicates whether value was found in the r.
func (r *Request) Value(key any) (value any, ok bool) { ... }
// Set sets the value to the r for the given key.
//
// The provided key must be comparable and should not be of type string or any
// other built-in type to avoid collisions between packages. Users should define
// their own types for keys.
func (r *Request) SetValue(key, value any) { ... }
The text was updated successfully, but these errors were encountered:
There is a demand to hold arbitrary data in the request scope, such as passing data between multiple handlers or storing parsed path variables by an HTTP router (e.g., #60227). This proposal suggests a general mechanism for storing arbitrary data in the
Request
struct.Background
Currently, the recommended approach for associating arbitrary data with a
Request
is to useRequest.WithContext
orRequest.Clone
along withcontext.WithValue
:The problem with this approach is that both methods create a copy of the
Request
(either shallow or deep), resulting in a performance hit. Additionally, users need to be cautious to avoid using the oldRequest
.It is worth mentioning that the primary purpose of
Request.WithContext
andRequest.Clone
is to enable users to control the entire lifetime of a request and its response: obtaining a connection, sending the request, and reading the response headers and body. Storing arbitrary data doesn't look like a good fit.Before the
context
package was introduced (i.e., before Go 1.7), storing arbitrary data inRequest
was basically not possible. So as a workaround, HTTP routers or web frameworks designed alternativeHandler
variants, likefunc(ResponseWriter, *Request, map[string]string)
, to pass their parsed path variables along withResponseWriter
andRequest
. Or simply abandonHandler
and introduce aContext
struct of their own. The result is added complexity.The #60227 also suggested a general mechanism to store arbitrary data in
Request
:However, in my opinion, the usage of the exported
map[string]any
has the following problems:map
introduces the risk of data tampering by other packages.Proposal
I propose to add an unexported
map[any]any
field named "vaules" to theRequest
struct. This field is used to hold arbitrary data within the request scope. Additionally, we also need add two exported methods to theRequest
struct:func(key any) (value any, ok bool)
as the getter andfunc(key, value any)
as the setter forRequest.values
.Implementation
This proposal will introduce the following API changes to the
net/http
package:The text was updated successfully, but these errors were encountered: