URL query strings
While we’re on the subject of routing, let’s update the snippetView
handler so that it accepts an id
query string parameter from the user like so:
Method | Pattern | Handler | Action |
---|---|---|---|
ANY | / | home | Display the home page |
ANY | /snippet/view?id=1 | snippetView | Display a specific snippet |
POST | /snippet/create | snippetCreate | Create a new snippet |
Later we’ll use this id
parameter to select a specific snippet from a database and show it to the user. But for now, we’ll just read the value of the id
parameter and interpolate it with a placeholder response.
To make this work we’ll need to update the snippetView
handler function to do two things:
It needs to retrieve the value of the
id
parameter from the URL query string, which we can do using ther.URL.Query().Get()
method. This will always return a string value for a parameter, or the empty string""
if no matching parameter exists.Because the
id
parameter is untrusted user input, we should validate it to make sure it’s sane and sensible. For the purpose of our Snippetbox application, we want to check that it contains a positive integer value. We can do this by trying to convert the string value to an integer with thestrconv.Atoi()
function, and then checking the value is greater than zero.
Here’s how:
package main import ( "fmt" // New import "log" "net/http" "strconv" // New import ) ... func snippetView(w http.ResponseWriter, r *http.Request) { // Extract the value of the id parameter from the query string and try to // convert it to an integer using the strconv.Atoi() function. If it can't // be converted to an integer, or the value is less than 1, we return a 404 page // not found response. id, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil || id < 1 { http.NotFound(w, r) return } // Use the fmt.Fprintf() function to interpolate the id value with our response // and write it to the http.ResponseWriter. fmt.Fprintf(w, "Display a specific snippet with ID %d...", id) } ...
Let’s try this out.
Restart the application, and try visiting a URL like http://localhost:4000/snippet/view?id=123
. You should see a response which looks like this:

You might also like to try visiting some URLs which have invalid values for the id
parameter, or no parameter at all. For instance:
http://localhost:4000/snippet/view
http://localhost:4000/snippet/view?id=-1
http://localhost:4000/snippet/view?id=foo
For all these requests you should get a 404 page not found
response.
The io.writer interface
The code above introduced another new thing behind-the-scenes. If you take a look at the documentation for the fmt.Fprintf()
function you’ll notice that it takes an io.Writer
as the first parameter…
func Fprintf(w io.Writer, format string, a ...any) (n int, err error)
…but we passed it our http.ResponseWriter
object instead — and it worked fine.
We’re able to do this because the io.Writer
type is an interface, and the http.ResponseWriter
object satisfies the interface because it has a w.Write()
method.
If you’re new to Go, then the concept of interfaces can be a bit confusing and I don’t want to get too hung up on it right now. It’s enough to know that — in practice — anywhere you see an io.Writer
parameter it’s OK to pass in your http.ResponseWriter
object. Whatever is being written will subsequently be sent as the body of the HTTP response.