All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 2m9s
91 lines
3.2 KiB
Markdown
91 lines
3.2 KiB
Markdown
---
|
|
title: "Learning Go: Day Seven"
|
|
date: 2024-05-07T08:00:00.0Z
|
|
tags:
|
|
- learning
|
|
- go
|
|
excerpt: "Kicking off the project by starting to build out an HTTP server"
|
|
---
|
|
|
|
So, yesterday I [decided I was going to build an uptime monitor and status page](/post/learning-go-day-six.md). To recap, here's the list of things I need to figure out how to do:
|
|
|
|
* Run a web server
|
|
* Render web pages with some dynamic content
|
|
* Store & read data from/to a database
|
|
* Send requets to a server & handle the responses
|
|
* Do all of the above on a periodic schedule
|
|
|
|
I've decided to call the project Oopsie, because that's what I'll say when things go red, and have setup the repo on [my git server](https://git.lewisdale.dev/lewis/oopsie). Like on [day one](https://lewisdale.dev/post/learning-go-day-one), I initialised my Go module:
|
|
|
|
```bash
|
|
go mod init lewisdale.dev/oopsie
|
|
```
|
|
|
|
And then created my `main.go` file.
|
|
|
|
## Creating a web server and handling my first request
|
|
|
|
Once again, Go's standard library comes to the rescue, as there are libraries for dealing with HTTP requests built right into the runtime. The [net/http package](https://pkg.go.dev/net/http) has functions for creating a server, listening for requests, and even serving static assets, which is really useful.
|
|
|
|
My first server, and request handler, is the ever-original "Hello, world":
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
|
|
func main() {
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("Hello, World!"))
|
|
})
|
|
http.ListenAndServe(":8000", nil)
|
|
}
|
|
```
|
|
|
|
It's fairly straightforward. The `http.HandleFunc` call registers a handler on the root URL, and `http.ResponseWriter.Write` outputs a string, which is converted to a slice of bytes. Then I start the actual server on port 8000 with `http.ListenAndServe`. Truly, riveting stuff.
|
|
|
|
|
|
## Pattern-matching handlers
|
|
|
|
The pattern syntax for `HandleFunc` is something called ServeMux, which I don't fully understand but fortunately the syntax is well-documented on the [package docs](https://pkg.go.dev/net/http#hdr-Patterns). It's not too dissimilar to most other URL pattern-matching libraries[^1]. Generally the syntax is:
|
|
|
|
1. `/` - match the root
|
|
2. `/something` - explictly match a URL
|
|
3. `/something-else/` - matching any URL where the path starts with `/something-else/`
|
|
4. `/route/{param}` - match a URL that starts with `/route/` and then has an arbitrary parameter in the second block of the path
|
|
|
|
You can also prefix the paths with either the request method, host name, or both. For example:
|
|
|
|
```go
|
|
http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("This was a GET request!"))
|
|
})
|
|
http.HandleFunc("POST /", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("This was a POST request!"))
|
|
})
|
|
```
|
|
|
|
So then if I perform a POST request:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8000
|
|
> This was a POST request!%
|
|
```
|
|
|
|
And then just a GET request:
|
|
|
|
```bash
|
|
curl http://localhost:8000
|
|
This was a GET request!%
|
|
```
|
|
|
|
That's a pretty powerful pattern-matcher to have straight out of the box. The only thing I'm less keen on is the `w.Write` syntax, but that's extremely minor. It'll be interesting to see how I get on.
|
|
|
|
[^1]: I still prefer Express-style pattern matching because inertia
|
|
|
|
|
|
|
|
|