From 1b1c3e663640cea7448ba2eb8ac77e96d10a5e60 Mon Sep 17 00:00:00 2001 From: Lewis Dale Date: Sun, 5 May 2024 08:41:32 +0100 Subject: [PATCH] Day 7: Project setup --- src/blog/posts/2024/5/learning-go-day-7.md | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/blog/posts/2024/5/learning-go-day-7.md diff --git a/src/blog/posts/2024/5/learning-go-day-7.md b/src/blog/posts/2024/5/learning-go-day-7.md new file mode 100644 index 0000000..e4d7369 --- /dev/null +++ b/src/blog/posts/2024/5/learning-go-day-7.md @@ -0,0 +1,90 @@ +--- +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 + + + +