Learning to Go: Part 4 - The Search Handler

Search for a command to run...

No comments yet. Be the first to comment.
Learn to write Go code by creating a basic web app that will provide a front-end for a public API
Oh hi! This is a multi-part tutorial series, please make sure you reads the posts in order! You can find the list at the bottom of the page. Structs Our app can now accept search terms from a user, but what do we do with them? To make our search engi...
Next up in my quest to Learn All The Things™ on the CNCF graduated projects page, we’re going to take a look at a lesser known Argo project: Argo Rollouts. In a nutshell, Argo Rollouts are a drop-in replacement for the Deployment object that provide ...

Continuing my efforts to explore and Learn All The Things™ on the CNCF graduated projects page, today I’m going to take a look at Argo Workflows, which is one of 4 tools in the Argo project overall (see my previous post on ArgoCD). Argo Workflows is ...

In my never-ending quest to Learn All The Things™, I’ve decided to take a tour through the Cloud Native Computing Foundation’s graduated projects page. I’m doing this for a few different reasons: Lea

This is the tenth post in a series exploring the features of GKE Enterprise, formerly known as Anthos. GKE Enterprise is an additional subscription service for GKE that adds configuration and policy management, service mesh and other features to supp...

This is the ninth post in a series exploring the features of GKE Enterprise, formerly known as Anthos. GKE Enterprise is an additional subscription service for GKE that adds configuration and policy management, service mesh and other features to supp...

Oh hi! This is a multi-part tutorial series, please make sure you reads the posts in order! You can find the list at the bottom of the page.
Now we’ve got a basic page up and running, in this step we’ll add the Search handler, which will actually deal with requests for searches.
First we’ll need to add fmt and net/url to our imports section:
import (
"fmt"
"html/template"
"net/http"
"net/url"
)
Next we’ll write our searchHandler function. First let’s add the skeleton of the function to main.go. You can add this after the indexHandler function.
func searchHandler(w http.ResponseWriter, r *http.Request) {
}
Just like our indexHandler, we define our function with its name and the inputs it will accept. Next, add the following code inside the function:
u, err := url.Parse(r.URL.String())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal server error"))
return
}
The first thing we do here is create a variable called u and assign to it the output of this function call: url.Parse(r.URL.String()). But what does that do?
Remember that r is passed into our function as a pointer to the http.Request. So that gives us lots of information about the request itself, and in this case we’re accessing the full URL of the request. We’re also using Parse which breaks it down into easier variables for us to access.
If you’ve looked in index.html you may have noticed that our search form uses the GET method. This means that our search parameters simply become part of the URL of the request. As they are part of the URL, we can access the search terms just by reading the URL.
But you may have also noticed that we don’t just assign u here, we also assign another variable called err. Then we immediately check to see if err is not nil, in which case we write out an internal server error, and quit.
This is a common programming pattern in Go for exception handling. If everything goes according to plan, we have our URL in the u variable. But if something bad happens, err will not be empty, or nil, and we can handle it gracefully.
Next, let’s move on to what we do with u:
params := u.Query()
searchKey := params.Get("q")
First we grab the parameters of the search by using the Query() function on u. This function grabs all of the parameters that are part of the URL path, without the website address. We can then access the parameters individually, which we do to get the searchKey.
Again, going back to our index.html, you’ll see that the text of our search is sent as the value of q. So we grab that paramater and assign to it searchKey. For now, rather than rendering any different HTML output, we’ll just ouput the value of searchKey to the console using fmt.Println:
fmt.Println("Search Query is: ", searchKey)
You’ll see the output come up in your terminal when you run the the program. That’s all the code we need for our searchHandler function.
The last thing to do is add our new searchHandler to our HTTP mux server. Add this before the HandleFunc line for indexHandler:
mux.HandleFunc("/search", searchHandler)
Now let’s test our search function:
go run main.go
Enter some search terms at http://localhost:8080 and observe them in the URL path in your browser. You’ll see a blank web page, but you should see your search terms in your terminal:
$ go run main.go
Search Query is: enterprise
Nicely done! We’ve added a new handler and learnt how to process parameters from an HTTP GET request. Now move on to part 5, where we’ll actually do something with that request.
Here’s the complete code of main.go if you need to check yours for errors:
package main
import (
"fmt"
"html/template"
"net/http"
"net/url"
)
var tpl = template.Must(template.ParseFiles("index.html"))
func indexHandler(w http.ResponseWriter, r *http.Request) {
tpl.Execute(w, nil)
}
func searchHandler(w http.ResponseWriter, r *http.Request) {
u, err := url.Parse(r.URL.String())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal server error"))
return
}
params := u.Query()
searchKey := params.Get("q")
fmt.Println("Search Query is: ", searchKey)
}
func main() {
mux := http.NewServeMux()
fs := http.FileServer(http.Dir("assets"))
mux.Handle("/assets/", http.StripPrefix("/assets/", fs))
mux.HandleFunc("/search", searchHandler)
mux.HandleFunc("/", indexHandler)
http.ListenAndServe(":8080", mux)
}