Merge pull request #565 from thewhitetulip/patch-6

added information about parameterized routing
This commit is contained in:
astaxie
2015-11-24 11:21:31 +08:00

View File

@@ -79,31 +79,99 @@ The following example shows how to implement a simple router.
package main
import (
"fmt"
"net/http"
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
#Routing
If you do not want to use a Router, you can still achieve what we wrote in the above section by replacing the second argument to `ListenAndServe` to nil and registering the URLs using a `HandleFunc` function which goes through all the registered URLs to find the best match, so care must be taken about the order of the registering.
sample code:
http.HandleFunc("/", views.ShowAllTasksFunc)
http.HandleFunc("/complete/", views.CompleteTaskFunc)
http.HandleFunc("/delete/", views.DeleteTaskFunc)
//ShowAllTasksFunc is used to handle the "/" URL which is the default ons
//TODO add http404 error
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
context := db.GetTasks("pending") //true when you want non deleted tasks
//db is a package which interacts with the database
if message != "" {
context.Message = message
}
homeTemplate.Execute(w, context)
message = ""
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
This is fine for simple applications which doesn't requires parameterized routing, what when you need that? You can either use the existing toolkits or frameworks, but since this book is about writing webapps in golang, we are going to teach how to handle this scenario as well.
When the match is made on the `HandleFunc` function, the URL is matched, so suppose we are writing a todo list manager and we want to delete a task so the URL we decide for that application is `/delete/1`, so we register the delete URL like this
`http.HandleFunc("/delete/", views.DeleteTaskFunc)`
`/delete/1` this URL matches closest with the "/delete/" URL than any other URL so in the `r.URL.path` we get the entire URL of the request.
http.HandleFunc("/delete/", views.DeleteTaskFunc)
//DeleteTaskFunc is used to delete a task, trash = move to recycle bin, delete = permanent delete
func DeleteTaskFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
id := r.URL.Path[len("/delete/"):]
if id == "all" {
db.DeleteAll()
http.Redirect(w, r, "/", http.StatusFound)
} else {
id, err := strconv.Atoi(id)
if err != nil {
fmt.Println(err)
} else {
err = db.DeleteTask(id)
if err != nil {
message = "Error deleting task"
} else {
message = "Task deleted"
}
http.Redirect(w, r, "/", http.StatusFound)
}
}
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
link: https://github.com/thewhitetulip/Tasks/blob/master/views/views.go#L170-#L195
In this above method what we basically do is in the function which handles the `/delete/` URL we take its compelete URL, which is `/delete/1`, then we take a slice of the string and extract everything which starts after the delete word which is the actual parameter, in this case it is `1`. Then we use the `strconv` package to convert it to an integer and delete the task with that taskID.
In more complex scenarios too we can use this method, the advantage is that we don't have to use any third party toolkit, but then again third party toolkits are useful in their own right, you need to make a decision which method you'd prefer. No answer is the right answer.
## Go code execution flow
Let's take a look at the whole execution flow.