HTTP handlers - when should I use return? - go

I'm a little confused about http handlers and handling something like errors or redirects.
For example, if I have to redirect because of some conditional check, should I be doing the following:
func SomeHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if thisThing != thatThing {
log.Print("thisThing not equal to thatThing - redirecting")
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return // <-- is this necessary?
}
}

The rule is: return when you're done processing, to prevent further processing.
In your case, a return is not necessary, because there is no further processing in your function. If you had further logic, though, you would want to return:
func SomeHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if thisThing != thatThing {
log.Print("thisThing not equal to thatThing - redirecting")
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return // <-- is this necessary?
}
w.Header().Add("Content-Type", "application/json")
// ... add a normal response
}
Without a return in this case, you'll send the headers to initiate a redirect, then you'll also send a normal JSON response. That is obviously not what you want, so the return is needed.
The astute reader will note that there would be other ways to accomplish this type of control flow. An else would be an option:
func SomeHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if thisThing != thatThing {
log.Print("thisThing not equal to thatThing - redirecting")
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
} else {
w.Header().Add("Content-Type", "application/json")
// ... add a normal response
}
}
However, as your conditions grow in complexity, a return is often going to be the most readable method. But it is ultimately a style choice at that point.

return is used for preliminary exit from the function and returning value to caller. As it was pointed before if there’s no more instruction function will exit but itself
When to use return?
One of key ideas of Go is readability - every program should be easy to read. So Go code is structured vertically - main flow goes as a straight line on the left from top to bottom. It’s often considered as a "good" or "basic" scenario. All diverts from it goes to right. Many diverts often are quite simple - for example it can be error workaround and exit.
For example you have a function whether number is positive:
func IsPositive(n int) bool {
if n <= 0 {
return false
}
return true
}
You may trace a main execution line on the left and catch keywords IsPositive below check digression if ... and at the bottom return true. As you can see we do not use else. We got it “for free” (without visual overloading) as a sole remaining option in our program. I can say functions structured this way require else quite rarely. Also you won’t find it very often keyword on standard code.

Related

Defer to outside a function

A common pattern I use is:
resource.open()
defer resource.close()
sometimes checking errors in between, which leads to:
err := resource.open()
if err != nil{
//do error stuff and return
}
defer resource.close()
Sometimes I will need multiple open/close resources in a row, leading to a variation of the previous 5 lines to be repeated one after another. This variation may be repeated verbatim several times in my code (where I need all the same resources).
It would be wonderful to wrap all this in a function. However doing so would close the resource as soon as the function call is over. Is there any way around this - either deferring to a "level up" the call stack or some other way?
One way to do this is using an "initializer" function with callback:
func WithResources(f func(Resource1, Resource2)) {
r1:=NewResource1()
defer r1.Close()
r2:=NewResource2()
defer r2.Close()
f(r1,r2)
}
func F() {
WithResources(func(r1 Resource1, r2 Resource2) {
// Use r1, r2
})
}
The signature of the function f depends on your exact use case.
Another way is to use a struct for a resource set:
type Resources struct {
R1 Resource1
R2 Resource2
...
}
func NewResources() *Resources {
r:=&Resources{}
r.R1=NewR1()
r.R2=NewR2()
return r
}
func (r *Resources) Close() {
r.R1.Close()
r.R2.Close()
}
func f() {
r:=NewResources()
defer r.Close()
...
}
It would be wonderful to wrap all this in a function.
Most probably a lot of people would hate reading such code. So "wonderful" might be very subjective.
However doing so would close the resource as soon as the function call is over.
Exactly.
Is there any way around this [...]?
No.

Parse parameter to bool or just use string in switch statement

I'm coming across a few situations where I would like to use routing to change some Is_Active fields in my database but I'm curious about performance.
Let's have a route handler as so:
func testHandler(r *mux.Router) {
r.HandleFunc("/test/{status}" statusHandler).Methods("GET")
}
Now that parameter will only ever be 0 or 1, unless the user tries something else but either way nothing will happen unless it's 0 or 1. My question is, is it faster to parse the string into a boolean which would involve bringing in the strconv package or would it be faster to just do a switch on the string?
Example of both:
func statusHandler(w http.ResponseWriter, r *http.Request) {
v := mux.Vars(r)
active, err := strconv.ParseBool(v["status"])
// Handle err
if active {
// Do something
} else {
// Do something else
}
}
or
func statusHandler(w http.ResponseWriter, r *http.Request) {
v := mux.Vars(r)
switch v["status"] {
case "0":
// Do something
case "1":
// Do something else
default:
// Throw 500 Error
}
}
You could see the source code of ParseBool here. It uses switch too but with more cases. If the compiler inlines its code it, speed should be very similar to your code. If you want a definite answer you should benchmark different cases.
In general I discourage you from paying attention to this small details. It's just matter of some nano seconds but it make your codes more difficult to understand. Begin optimizations with profiling your code to find hotspots that take a lot of time and fix them.

Meaning of underscore in a Go function parameter

Came accross the below function here. I noticed the last parameter is identified with _. What is the intent of this pattern?
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
It means "ignore that parameter", the reason that they still need the last parameter here is because they want to pass it as type Handle to the function GET, which has the signature:
type Handle func(http.ResponseWriter, *http.Request, Params)
If you simply pass something like func Index(w http.ResponseWriter, r *http.Request) it will not be treated as type Handle.
_ is the blank identifier. It's in the signature to show that the value doesn't get used, so the signature will still match the methods of the interface.
Using "_" in place of a parameter name fulfills the obligations of a higher-level "function as a parameter" without getting a warning about an unused parameter. In your case, I believe the compiler is told to ignore all incoming "POST" data, thus in effect reducing the request to the functionality of a "GET".
As others pointed out, it is a blank identifier. For instance, consider the following example:
func main() {
nums := []int{5, 3, 4}
max := nums[0]
for _, num := range nums {
if num > max {
max = num
}
}
fmt.Println("max:", max)
}
If you aren't going to use an index value, you can just ignore storing it by using _ instead of a variable name.=.

Basic web tweaks that all applications should have

Currently my web app is just a router and handlers.
What are some important things I am missing to make this production worthy?
I believe I have to set the # of procs to ensure this uses maximum goroutines?
Should I be using output buffering?
Anything else you see missing that is best-practise?
var (
templates = template.Must(template.ParseFiles("templates/home.html")
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", WelcomeHandler)
http.ListenAndServe(":9000", r)
}
func WelcomeHandler(w http.ResponseWriter, r *http.Request) {
homePage, err := api.LoadHomePage()
if err != nil {
}
tmpl := "home"
renderTemplate(w, tmpl, homePage)
}
func renderTemplate(w http.ResponseWriter, tmpl string, hp *HomePage) {
err := templates.ExecuteTemplate(w, tmpl+".html", hp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
You don't need to set/change runtime.GOMAXPROCS() as since Go 1.5 it defaults to the number of available CPU cores.
Buffering output? From the performance point of view, you don't need to. But there may be other considerations for which you may.
For example, your renderTemplate() function may potentially panic. If executing the template starts writing to the output, it involves setting the HTTP response code and other headers prior to writing data. And if a template execution error occurs after that, it will return an error, and so your code attempts to send back an error response. At this point HTTP headers are already written, and this http.Error() function will try to set headers again => panic.
One way to avoid this is to first render the template into a buffer (e.g. bytes.Buffer), and if no error is returned by the template execution, then you can write the content of the buffer to the response writer. If error occurs, then of course you won't write the content of the buffer, but send back an error response just like you did.
To sum it up, your code is production ready performance-wise (excluding the way you handle template execution errors).
WelcomeHandler should return when err != nil is true.
Log the error when one is hit to help investigation.
Place templates = template.Must(template.ParseFiles("templates/home.html") in the init. Split it into separate lines. If template.ParseFiles returns an then error make a Fatal log. And if you have multiple templates to initialize then initialize them in goroutines with a common WaitGroup to speed up the startup.
Since you are using mux, HTTP Server is too clean with its URLs might also be good to know.
You might also want to reconsider the decision of letting the user's know why they got the http.StatusInternalServerError response.
Setting the GOMAXPROCS > 1 if you have more the one core would definitely be a good idea but I would keep it less than number of cores available.

Safely terminating a request outside of handlefunc

How can I safely terminate a request outside of a dispatcher (handlefunc) without an explicit return in handlefunc? In the code below, I end up seeing both "Bad request" and "Not found" in my response, but I'd like f() to abort the request -- so I wouldn't have to clutter dispatcher with error checks and returns.
package main
import (
"net/http"
)
func f(w http.ResponseWriter, r *http.Request, k string) string{
if v, ok := r.Form[k]; !ok{
http.Error(w, "Bad request", http.StatusBadRequest)
return ""
}else{
return v[0]
}
}
func main(){
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
foo := f(w, r, "foo") //make sure foo is passed, if not abort with "bad request" without requiring an explicit return here
bar := f(w, r, "bar") //make sure bar is passed, if not abort with "bad request" without requiring an explicit return here
//lookup in db...
//abort with a 404 if not found in db
http.NotFound(w, r)
})
http.ListenAndServe(":6000", nil)
}
The above was an attempt to refactor the following:
func main(){
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
if foo,ok:=r.Form["foo"];!ok{
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
if bar,ok:=r.Form["bar"];!ok{
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
//lookup in db...
//abort with a 404 if not found in db
http.NotFound(w, r)
})
http.ListenAndServe(":6000", nil)
}
I don't think trying to abort from within a function is what you want to do here. Instead, make the validation less repetitive. You could write something like package validate with func Require(form url.Values, required ...string) error, and, in your handler, if err := validate.Require(r.Form, "foo", "bar"); err != nil { ... }.
Or you could do a more general validator: maybe take a map of field name to validation type (number, string, etc.) and return another map that's nil if no errors and {"fieldName": error} otherwise.
Redditors talked some about this and one wrote a library that uses struct tags to validate. There's another struct-based validation implementation in a new form rendering/validation toolkit. (I've tried neither.) The Redditors raised the tricky question of how much you can abstract validation before getting a "framework" that gets in the way when your app gets more complicated, and I don't have a simple answer.
There are cases where something really unexpected happens and the server can't do anything better than give the user an opaque "500 Internal Server Error" response, but the problem manifests deep below your HandlerFunc. The canonical example is a programming error like a nil pointer dereference. The advice from the Go team is to use panic when "the error is unrecoverable." (A tool like negroni.Recovery can help log panics, etc.)
Unnecessary panics are lame because:
panics make it easier to forget necessary error handling entirely, because it's implied that things can panic but explicit they can err
panic/recover error handling code is ugly and hard to maintain
panics introduce inconsistency with the standard err returns
panics use stack unwinding, which is much slower than normal control flow
I did a quick grep and essentially all calls to panic in the standard library and other official repositories are there to catch programming/internal errors, not crash on surprise external conditions like bad input or I/O errors. Things like file/object not found or invalid user input can usually be handled more gracefully than by crashing the request (it's at least possible to return a specific error), and they aren't really unexpected (it's the Internet; you'll get invalid input and requests for things that don't exist). So panic is not for normal request aborts--it's for crashing when what "can't happen" has happened and the request can't continue.
Again, here, I'd handle invalid input with a standard if block in the handler function, but rearrange your validation so that it doesn't require a lot of code per required argument.

Resources