custom header

Oh Hi!

So… things are getting more difficult as I go on with the challenge, yet I can start new things faster, understand more concepts etc. I have made my mind to start READING new content, as Let’s Go book, which is my next step to follow, but I’m open to get into other documentation matters or the all mighty “Effective Go” thingy.

19/08/2024 & 21/08/2024 - Turn to page 394… (Day 21)

Overall I took it easy and started of smoothly enough, covering the first ~50 pages. At the time of writing I have the following code, covering almost entirely the first two chapters of the book.

Then again, I joined two days since both were focused into going through this book.

package main

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
)

func home(w http.ResponseWriter, r *http.Request) {
	// Add elements to HEADER
	// MUST be before w.Write
	// Key, Value
	w.Header().Add("Server", "Go")

	//w.Write([]byte("Hello from Patatabox"))
	//io.WriteString(w, "Hello world")
	fmt.Fprint(w, "Hello from Patatabox")
}

// Second handler function - Non-home result
func snippetView(w http.ResponseWriter, r *http.Request) {
	// Extract value of the id wildcard from the request -- r.PathValue()
	// Convert it to an int -- strconv.Atoi()
	// If can't convert -- error 404
	id, err := strconv.Atoi(r.PathValue("id"))
	if err != nil || id < 1 {
		http.NotFound(w, r)
		return
	}
	/*
		msg := fmt.Sprintf("Display a specific snippet with ID %d...", id)
		w.Write([]byte(msg))
	*/
	// Writing reponse body in a more efficient way
	fmt.Fprintf(w, "Display a specific snippet with ID %d...", id)
}

// Third handler function - Non-home result
func snippetCreate(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Display a form...hehe"))

}

func snippetCreatePost(w http.ResponseWriter, r *http.Request) {
	// Change header result (HTTP Code)
	//w.WriteHeader(201)
	w.WriteHeader(http.StatusCreated)

	w.Write([]byte("Save a new snippet..."))

}

func main() {
	// Initialize router -- servemux
	mux := http.NewServeMux()
	mux.HandleFunc("GET /{$}", home) // HandleFunc assumes already the pass of a ResponseWritter and a Request
	// Wildcard route patterns
	/*
		Define create more flexible routing rules
			- Only 1 per segment
			- each path segment (the bit between forward slash characters)
			can only contain one wildcard

		The rule for preference between two similar routes (i.e /post/{id} - /post/edit)
		 	- the most specific route pattern wins.
			- Therefore "/post/edit" is the more specific route
			pattern and will take precedent.


		Potential edge case - overlapping route patterns
		i.e "/post/new/{id}" and "/post/{author}/latest" - they both match the
		request path /post/new/latest

			- Go’s servemux considers the patterns to conflict, and will panic at
			runtime when initializing the routes.
			- it’s generally good practice to keep overlaps to a minimum or
			avoid them completely.

	*/
	mux.HandleFunc("GET /snippet/view/{id}", snippetView) // Calls to non home/root results
	mux.HandleFunc("GET /snippet/create", snippetCreate)  // Dollar means match a single slash, followed by nothing else

	// hostname matching
	// It is possible to include hostnames in route patterns, useful
	// if using Go to redirect HTTP request to multiple sites or services
	/*
		mux := http.NewServeMux()
			mux.HandleFunc("foo.example.org/", fooHandler)
			mux.HandleFunc("bar.example.org/", barHandler)
			mux.HandleFunc("/baz", bazHandler)
	*/
	mux.HandleFunc("POST /snippet/create", snippetCreatePost)

	// Print log as server is starting
	log.Print("Starting server on port :4000...")

	// Start server
	// Pass two parameters - TCP Network address to listen on
	// and servemux (router). Meaning port as destination and router
	// as "origin"
	err := http.ListenAndServe(":4000", mux)
	// err var is always not nil -- log fatal to record any error happening
	log.Fatal(err)

}

I’d say that now I have a better comprehension of how does most basic net/http code work with Golang, which was a previous struggling point, as I could not follow Go web examples that easily.

24/08/2024 - Day 22

Let’s get hands on with a project! shall we? I started with a url shortener! My main reason for this is that it would help me revisit some concepts I’ve just learned via Let’s Go book, as well as introducing other aspects of Golang that I might have missed to this point, or may be not used that much.

For reference, I followed a specific youtube video, yet then I refactorized the code to not use any external framework or library as Gorilla Mux. Video

package main

import (
	"crypto/md5"
	"fmt"
	"log"
	"net/http"
	"strings"
)

var urlStore = make(map[string]string)

func shorten(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	url := r.Form.Get("url")
	shortURL := fmt.Sprintf("%x", md5.Sum([]byte(url)))[:5]
	urlStore[shortURL] = url
	fmt.Fprintf(w, "http://localhost:8080/%s\n", shortURL)
}

func redirect(w http.ResponseWriter, r *http.Request) {
	shortURL := strings.TrimPrefix(r.URL.Path, "/")
	oUrl, ok := urlStore[shortURL]
	if ok {
		http.Redirect(w, r, oUrl, http.StatusMovedPermanently)
	} else {
		http.NotFound(w, r)
	}
}

func main() {
	r := http.NewServeMux()
	r.HandleFunc("POST /create", shorten)
	r.HandleFunc("GET /{shortURL}", redirect)
	log.Print("Starting server on port :8080...")
	log.Fatal(http.ListenAndServe(":8080", r))

}

functioning code

As for now it works like a charm! Yet I find some potential improving lines, potentially implemented over time, once I get more experience with this programming language:

	- Make it as a web service with GUI (Adding styles etc)
	- Add shortened urls to a CSV file or a database, so later on those can be checked again and quickly accessed