0

I am looking for a simple way to create dynamic routes with net/http (no routers like mux etc.) Here is my current code:

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

        pages := r.URL.Query()["q"]
        if len(pages) == 0 {
            fmt.Fprintf(w, "§§§§§§§§§§ You need to specify a page §§§§§§§§§§")
            return
        }

        page := pages[0]

        var a Page
        err := db.QueryRow("SELECT * FROM pages where page = ?", page).Scan(&a.Page, &a.Date, &a.Url)
        a.Year = time.Now().UTC().Year()
        if err != nil {
            if err == sql.ErrNoRows {
                fmt.Fprintf(w, "Page %s not found", page)
                return
            } else {
                fmt.Fprintf(w, "Some error happened")
                return
            }
        }

        http.Redirect(w, r, a.Url, 301)

    })

So now the URL sample.com/?q= works dynamically. My objective is to work without having to use r.URL.Query()["q"] so directly /pagename

This is not a duplicate of Go url parameters mapping because it is a single level (not nested levels) AND many answers in that question refer to using an external library.

devnull
  • 2,752
  • 1
  • 21
  • 38
  • But the answer you linked basically answers the question - without using external libraries you have to parse `r.URL.Path` manually and return a response based on that. – Z. Kosanovic Oct 17 '20 at 23:18
  • @Z.Kosanovic how ? do I have to replace pages := with getCode function as in the other reply ? I don't know it it would work in this case. Feel free to provide a full reply. That question is also 2+ years old and I am using the latest go. – devnull Oct 17 '20 at 23:57
  • @Z.Kosanovic also I tried the only valid answer and func getCode(r *http.Request, defaultCode int) returns 2 values including an int which is irrelevant in my question. – devnull Oct 18 '20 at 00:10
  • Literally just do this `page := r.URL.Path[1:]` and then whatever logic you want to do. – Z. Kosanovic Oct 18 '20 at 00:47
  • @Z.Kosanovic this is working thank you! but nowhere in the previous answer I see this. There is all about looping and creating a new function. I suggest you prepare a short answer so others can benefit from this. – devnull Oct 18 '20 at 08:31

2 Answers2

3

If you don't want to use any third-party libraries, you have to handle the parsing of the path yourself.

For start, you can do this:

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

    page := r.URL.Path[1:]
    // do whatever logic you want
    // mind that the page could be "multi/level/path/" as well
})
Z. Kosanovic
  • 727
  • 4
  • 10
1

You can use http.HandleFunc. In this function, a pattern ending in a slash defines a subtree. You can register a handler function with the pattern "/page/" like the below example.

package main

import (
    "net/http"
    "fmt"
)

func handler(w http.ResponseWriter, r *http.Request) {
    if is_valid_page(r.URL) {
        fmt.Fprint(w, "This is a valid page")
    } else {
        w.WriteHeader(http.StatusNotFound)
        fmt.Fprint(w, "Error 404 - Page not found")
    }
}

func is_valid_page(page string) {
    // check here if page is valid from url 
}


func main() {
    http.HandleFunc("/page/", handler)
    http.ListenAndServe(":8080", nil)
}

more info you can find here: https://golang.org/pkg/net/http/#ServeMux

  • Thanks for the reply but I am confused: as you see in my question I am already using http.HandleFunc as a routing mechanism. The issue is about dynamic creating routes based on the db entries e.g. /:page_name as you can see on my question – devnull Oct 18 '20 at 08:22
  • the :page_name is a placeholder for any pagename means, this handler except eg localhost:8080/page/home or localhost:8080/page/page1 etc. What you then need to do is only like your accepted answer do – Stefan Wuthrich - Altafino Oct 19 '20 at 04:04