Golang 1.7 version of the context library is used by many standard library modules, such as net/http and OS modules, using these native modules, we do not need to write the context of the manager, directly call the function interface can be implemented, Using context, we can implement some variable management such as the request's declaration period, perform some operation timeouts and so on. Save Context Object
Here we look at a simple example of how to use the context feature to implement the object save of contexts, where we write a simple HTTP server with login and exit, stateful check routing (check if the user is logged in)
Func Main () {
mux:=http. Newservemux ()
mux. Handlefunc ("/", Statushandler)
mux. Handlefunc ("/login", Loginhandler)
mux. Handlefunc ("/logout", Logouthandler)
contextedmux:=addcontextsupport (MUX)
log. Fatal (http. Listenandserve (": 8080", Contextedmux))
}
The Addcontextsupport is a middleware used to bind a context into the original handler, and all requests must pass through the middleware before they can enter their own route processing. The specific implementation code is as follows:
Func addcontextsupport (next http. Handler) http. handler{
return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {
log. Println (R.method, "-", R.requesturi)
Cookie, _: = R.cookie ("username")
if cookie! = Nil {
CTX: = context. Withvalue (R.context (), "username", cookies. Value)
//Withcontext Returns a shallow copy of R with it context changed
//to CTX. The provided CTX must be non-nil.
Next. Servehttp (W, R.withcontext (CTX))
} else {
next. Servehttp (W, R)}}
)
}
The middleware can print the method of each request and the URL of the request, and then obtain the requested cookie value, if the cookie is empty, then continue to pass to the corresponding route handler function, otherwise save the value of the cookie to the context, note the context here () Is the method of the Request object, which creates a new context object (if the context is empty), context. The Withvalue () function saves the key and value in the new context object and returns the object.
The remaining route handler codes are as follows: the login Route Loginhandler () used to hold the cookie, and the exit route handler function Logouthandler () that deletes the cookie.
Func Loginhandler (w http. Responsewriter,r *http. Request) {
expitation: = time. Now (). ADD (24*time. Hour)
var username string
if Username=r.url. Query (). Get ("username"); username== "" {
username = "Guest"
}
cookie:=http. Cookie{name: "username", value:username,expires:expitation}
http. Setcookie (W,&cookie)
}
func Logouthandler (w http. Responsewriter, R *http. Request) {
Expiration: = time. Now (). Adddate (0, 0,-1)
Cookie: = http. Cookie{name: "username", Value: "Alice_cooper@gmail.com", expires:expiration}
http. Setcookie (W, &cookie)
}
Here, when we request/login, we can pass a parameter username to the function, such as/login?username=alice, which defaults to the "guest" user. The cookie set is valid for 1 days, and we only need to set the cookie to the previous date when we delete it.
Another key part is the Statushandler () route handler function that reads the contents of the context save, which calls R. The context () Gets the request, and if we execute login, then we have set the object in the middleware, so the request will see if the context object holds an object named username and, if there is one, responds to a welcome page. Otherwise, tell the user not to log in.
Func Statushandler (w http. Responsewriter,r *http. Request) {
if Username:=r.context (). Value ("username"); username!=nil{
W.writeheader (http. Statusok)
W.write ([]byte ("Hi username:" +username. ( string) + "\ n"))
}else{
w.writeheader (http. Statusnotfound)
W.write ([]byte ("not logged in")
}
}
We can not only save a simple type in context, we can save any type of data, because the object returned by value () is a interface{} object, so we need to convert it to use. Timeout Processing
For simple saving and passing objects, it is convenient to use the context, but the use of the library is not only to save variables, but also to create a time-out and cancellation behavior, such as our web to request other resources, but the processing of the resource is time-consuming, we can not foresee when to return, If the user times out, it is not very good, so we need to create a timeout operation, actively determine whether to timeout, and then pass a suitable behavior to the user.
Here we now add a long-running job route to the route
Mux. Handlefunc ("/longjob", Jobwithcancelhandler)
Specifically, our handler will use Withcancel () to return a new (if not created) or previously saved context, and a cancel object that can be used to perform the cancel operation manually. In addition, we can specify the length of this task in the URL, such as/longjob?jobtime=10, which represents the execution of the simulated task for more than 10 seconds. The function that performs the task Longrunningcalculation () returns a Chan who writes a done string after the execution time expires.
In handler we can use the SELECT statement to listen for two non-cached channel, blocking until there is data written to any channel. For example, in the code we set the timeout to 5 seconds, while the task executes 10 seconds, then 5 seconds expires after CTX. Done () writes the data because of a call to cancel () so that the handler exits because of a timeout. Otherwise, perform a normal job processing and get the passed "Done" exit.
func longrunningcalculation (timecost int) Chan string{Result:=make (chan string) go func () {time. Sleep (time. second* (time. Duration (Timecost))) result<-"Done"} () return result} Func Jobwithcancelhandler (w http). Responsewriter, R * http. Request) {var ctx context. Context var cancel context. Cancelfunc var jobtime string if Jobtime=r.url. Query (). Get ("Jobtime"), jobtime== "" {jobtime = "ten"} timecost,err:=strconv. Atoi (jobtime) if err!=nil{timecost=10} log. Println ("Job would cost:" +jobtime+ "s") Ctx,cancel = context. Withcancel (R.context ()) defer cancel () go func () {time. Sleep (5*time. Second) Cancel ()} () select{case <-ctx. Done (): Log. Println (CTX. ERR ()) return case result:=<-longrunningcalculation (timecost): IO. WriteString (W,result)} return}
This is the use of the context of some basic way, in fact, there are many functions of the context is not involved here, such as Withtimeout and Withdeadline, but the use of the comparison is similar, here is not an example.
In addition: My personal website jsmean.com welcome everyone to visit.