#How to avoid using Gorilla mux for a url shortener project

25 messages · Page 1 of 1 (latest)

spring zinc
#

Hi!

So i followed this video: https://www.youtube.com/watch?v=HBirbA5-xiA

And since i'm yet no Go expert by far, I want to avoid any framework or library for the time being, just learn all around without any "help" kind of approach, yet I found myself needing GorillaMux for my current code:

package main

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

    "github.com/gorilla/mux"
)

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) {
    vars := mux.Vars(r)
    oUrl, ok := urlStore[vars["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))

}

It is pretty much the same as the video shows but I did it to use net/http instead of mux, yet I found myself unable to remove the vars := mux.Vars(r) line, since I don't really understand what is he doing there, overall I get how this works yet it needs quite some comments and refinement imo, but the thing is, can i replace that line? If so, how? (let me know as in "you can define this or do that", not the code in on itself please

Lets build a URL shortener with Golang and Gorilla Mux

Learn to build real software projects on CodeCrafters
https://app.codecrafters.io/join?via=TheBuilder-software

▶ Play video
austere lark
inland prism
#

isnt it a path argument what you need for redirecting? check what gorilla mux doc says that vars is or does but it’d be request.url.path or some such what you need

austere lark
#

you can use the request path value

which work similarly to mux.Vars

inland prism
#

that's the new stuff but it's also unecessary here

austere lark
#

which is what pathvalue is

inland prism
#

well my point is you can get this working easily even in earlier go version as the short url is r.RequestURI or r.URL.Path

#

just replacing:

    r.HandleFunc("POST /create", shorten)
    r.HandleFunc("GET /{shortURL}", redirect)

by

    r.HandleFunc("/create", shorten)
    r.HandleFunc("/", redirect)
austere lark
#

furthermode, GET /{shortURL} will return a 404 if you use multiple slashes e.g /a/b/c

while your version would allow this, and promptly cause an error

inland prism
#

there is no difference betwe "/a/b/c" and "/foo" that is not found

austere lark
#

for the current purpose no, but one is much more convenient to use

inland prism
#

no I do mean you have to handle whether the short url exists or not no matter what

#

and a less trivial (and more correct) shortening might well use / as a part of the character set

austere lark
inland prism
#

the original question is " I want to avoid any framework or library for the time being, just learn all around without any "help" kind of approach" I think that means "use fewest possible features that make something work" which is what my answer covers, but sure to just replace exactly 1 line (instead of ... understanding what's going on) you can use the newer r.PathValue - either way hopefully OP learned something

spring zinc
spring zinc
#

for reference, i looked up other examples and rewatched the whole thing, and my current (working) code is as follows.

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, "/")
    //vars := mux.Vars(r)
    //oUrl, ok := urlStore[vars["shortURL"]]
    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))

}

I'm open to any feedback or like improvement advice you would like to give me, otherwise, I will shortly mark this help question as solved.

Thank you very much for your time!!!

Sidenote: I wanted to preserve as much as the original script as possible, since I've just recently started to learn web-related development with Let's Go book, the GET and POST way matches the actual explanation I've been given, thus made more sense to make it that way.

inland prism
#

lgtm but to make it work with go1.21 (you don’t have to) you’d have change the handler registration as I mentioned earlier, conversely if you rely on {shortURL} syntax you might as well use PathValue() to get it

spring zinc
#

i'm on Go's latest version btw

inland prism
#

in professional settings it’s not uncommon to be some versions behind, specially not use any .0 / just released version

#

but with 1.23 just out it’s fair to start using 1.22 features

spring zinc
#

i saved some of the provided links and documentation, and sort of understand what they meant