From 8c5218836d05112d7804abfc362df9d95466637f Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 11:53:25 -0500 Subject: [PATCH 1/6] Create basic-http-routing-in-golang.json --- posts/basic-http-routing-in-golang.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 posts/basic-http-routing-in-golang.json diff --git a/posts/basic-http-routing-in-golang.json b/posts/basic-http-routing-in-golang.json new file mode 100644 index 0000000..126e5b0 --- /dev/null +++ b/posts/basic-http-routing-in-golang.json @@ -0,0 +1,9 @@ +{ + "title": "Basic HTTP Routing in Golang - Levi Olson", + "permalink": "/posts/basic-http-routing-in-golang", + "created_at": "2018-05-09T11:52:50-06:00", + "created_at_short": "2018-05-09", + "post_title": "basic http routing in golang", + "active": "posts", + "content_file": "basic-http-routing-in-golang.html" +} From f2ca272d1f837eb7c12a17e2df56a70d261bd66b Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 14:09:57 -0500 Subject: [PATCH 2/6] Beginning stages of a new post on basic http routing in Golang --- posts/basic-http-routing-in-golang.html | 0 posts/basic-http-routing-in-golang.md | 32 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 posts/basic-http-routing-in-golang.html create mode 100644 posts/basic-http-routing-in-golang.md diff --git a/posts/basic-http-routing-in-golang.html b/posts/basic-http-routing-in-golang.html new file mode 100644 index 0000000..e69de29 diff --git a/posts/basic-http-routing-in-golang.md b/posts/basic-http-routing-in-golang.md new file mode 100644 index 0000000..65298dd --- /dev/null +++ b/posts/basic-http-routing-in-golang.md @@ -0,0 +1,32 @@ +# Basic HTTP Routing in Golang + +Golang is incredibly powerful. Its standard library has so much to offer, but I think other languages have encouraged the use of external libraries for even the most basic tasks. For example, with JavaScript, most inexperienced developers seem to use jQuery to do simple tasks like selecting an element and replacing its contents. When you and I both know jQuery is way overkill for such a task. [See my article on Vanilla JS basics.](/coming-soon). + +I believe that in order to be considered an expert in a language, you must at least be able to demonstrate using the core language to achieve your goal. In our current case, HTTP routing. Now to be clear, I don't think you need to write everything from scratch all the time, but you should have a firm grasp on what is available by the core language, and when you are better suited to use an external library. If you are looking for more advanced HTTP routing, then I would suggest using something like [gin](https://github.com/gin-gonic/gin). + +Enough ranting, let's get to it. + +## Assumptions + +I assume you have basic knowledge of the Go language at this point, so if not, it might be worth searching for some entry level basics first. See [A Tour of Go](https://tour.golang.org). + +## Let's begin + +Here is our basic folder structure for this basic http routing example: + + basic-http-routing-in-golang/ + main.go + +As a starting point our `main.go` file looks like this: + + package main + + import ( + "fmt" + _ "net/http" + ) + + func main() { + fmt.Println("Hello HTTP") + } + From ebab1b691ec23b2273d4b82efdd4a616c19a72fd Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 15:05:07 -0500 Subject: [PATCH 3/6] Updates to basic http routing in golang example --- posts/basic-http-routing-in-golang.md | 39 ++++++++++++++++++++++++++ posts/step2-browser-output.png | Bin 0 -> 7270 bytes 2 files changed, 39 insertions(+) create mode 100644 posts/step2-browser-output.png diff --git a/posts/basic-http-routing-in-golang.md b/posts/basic-http-routing-in-golang.md index 65298dd..172576d 100644 --- a/posts/basic-http-routing-in-golang.md +++ b/posts/basic-http-routing-in-golang.md @@ -12,6 +12,8 @@ I assume you have basic knowledge of the Go language at this point, so if not, i ## Let's begin +### Step 1 + Here is our basic folder structure for this basic http routing example: basic-http-routing-in-golang/ @@ -30,3 +32,40 @@ As a starting point our `main.go` file looks like this: fmt.Println("Hello HTTP") } +### Step 2 + +Now starting at a very basic level, we can leverage the [`http.HandleFunc`](https://golang.org/pkg/net/http/#HandleFunc) method. + +It is very simple to use and its signature is easy to understand. + + func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) + +Which basically means, `http.HandleFunc("/url", routingFunction)` where `routingFunction` looks like this: + + func routingFunction(w http.ResponseWriter, req *http.Request) { + fmt.Fprint(w, "Hello HTTP") + } + +With `fmt.Fprint()` we can pass an `http.ResponseWriter` and a message to display. Our browser will now look like this when we visit the `/url` endpoint. + +![](step2-browser-output.png) + +Here is what `main.go` looks like at this point: + + package main + + import ( + "fmt" + "log" + "net/http" + ) + + func main() { + http.HandleFunc("/hello", HelloHTTP) + log.Fatal(http.ListenAndServe(":8080", nil)) + } + + func HelloHTTP(w http.ResponseWriter, req *http.Request) { + fmt.Fprint(w, "Hello HTTP") + } + diff --git a/posts/step2-browser-output.png b/posts/step2-browser-output.png new file mode 100644 index 0000000000000000000000000000000000000000..20de4fc1d1bf98765567076cc2c2e59cfb639304 GIT binary patch literal 7270 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 literal 0 HcmV?d00001 From a13a8c872588de64cde64800ff8aa6e1c972a257 Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 16:12:10 -0500 Subject: [PATCH 4/6] Updating Basic HTTP routing article --- posts/basic-http-routing-in-golang.html | 179 ++++++++++++++++++++++++ posts/basic-http-routing-in-golang.md | 93 +++++++++++- 2 files changed, 269 insertions(+), 3 deletions(-) diff --git a/posts/basic-http-routing-in-golang.html b/posts/basic-http-routing-in-golang.html index e69de29..c82535f 100644 --- a/posts/basic-http-routing-in-golang.html +++ b/posts/basic-http-routing-in-golang.html @@ -0,0 +1,179 @@ +

Basic HTTP Routing in Golang

+

Golang is incredibly powerful. Its standard library has so much to offer, but I think other languages have encouraged the + use of external libraries for even the most basic tasks. For example, with JavaScript, most inexperienced developers + seem to use jQuery to do simple tasks like selecting an element and replacing its contents. When you and I both know + jQuery is way overkill for such a task. + See my article on Vanilla JS basics..

+

I believe that in order to be considered an expert in a language, you must at least be able to demonstrate using the core + language to achieve your goal. In our current case, HTTP routing. Now to be clear, I don't think you need to write everything + from scratch all the time, but you should have a firm grasp on what is available by the core language, and when you are + better suited to use an external library. If you are looking for more advanced HTTP routing, then I would suggest using + something like + gin.

+

Enough ranting, let's get to it.

+

Assumptions

+

I assume you have basic knowledge of the Go language at this point, so if not, it might be worth searching for some entry + level basics first. See + A Tour of Go.

+

Let's begin

+

Step 1

+

Here is our basic folder structure for this basic http routing example:

+
    basic-http-routing-in-golang/
+        main.go
+
+

As a starting point our + main.go file looks like this:

+
    package main
+
+    import (
+        "fmt"
+        _ "net/http"
+    )
+
+    func main() {
+        fmt.Println("Hello HTTP")
+    }
+
+

Step 2

+

Now starting at a very basic level, we can leverage the + + http.HandleFunc + method.

+

It is very simple to use and its signature is easy to understand.

+
    func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
+
+

Which basically means, + http.HandleFunc("/url", routingFunction) where + routingFunction looks like this:

+
    func routingFunction(w http.ResponseWriter, req *http.Request) {
+        fmt.Fprint(w, "Hello HTTP")
+    }
+
+

With + fmt.Fprint() we can pass an + http.ResponseWriter and a message to display. Our browser will now look like this when we visit the + /url endpoint.

+

+ Browser Output for Step 2 - Hellp HTTP +

+

Here is what + main.go looks like at this point:

+
    package main
+
+    import (
+        "fmt"
+        "log"
+        "net/http"
+    )
+
+    func main() {
+        http.HandleFunc("/hello", helloHTTP)
+        log.Fatal(http.ListenAndServe(":8080", nil))
+    }
+
+    func helloHTTP(w http.ResponseWriter, req *http.Request) {
+        fmt.Fprint(w, "Hello HTTP")
+    }
+
+

Now we could stop there, as this is a "basic" http routing example, but I think it isn't quite useful as an example + yet, until we start to see something slightly more practical.

+

Step 3

+

For starters, let's add a + NotFound page when we don't match a pattern in + HandleFunc.

+

Here is what + main.go looks like after that:

+
    package main
+
+    import (
+        "fmt"
+        "log"
+        "net/http"
+    )
+
+    func main() {
+        http.HandleFunc("/hello", helloHTTP)
+        http.HandleFunc("/", notFound)
+        log.Fatal(http.ListenAndServe(":8080", nil))
+    }
+
+    func helloHTTP(w http.ResponseWriter, req *http.Request) {
+        fmt.Fprint(w, "Hello HTTP")
+    }
+
+    func notFound(w http.ResponseWriter, req *http.Request) {
+        http.NotFound(w, req)
+    }
+
+

This will match + /hello and use the + HelloHTTP method to print "Hello HTTP" to the browser. Any other urls will get caught by the + / pattern and be given the + http.NotFound response to the browser.

+

So that works, but I think we can go further.

+

Step 4

+

We need to give ourselves something more specific than the simple contrived + /hello endpoint above. So let's assume we are needing to get a user profile and we will use the url + /user/:id where + :id is an identifier used to get the user profile from our persistance layer (i.e. our database).

+

We'll start by creating a new method for this GET request called + userProfile:

+
    func userProfile(w http.ResponseWriter, req *http.Request) {
+        userID := req.URL.Path[len("/user/"):]
+        fmt.Fprintf(w, "User Profile: %q", userID)
+    }
+
+

Notice that we get the URL from the + req variable and we treat the string returned from + req.URL.Path as a byte slice to get everything after the + /user/ in the string. + Note: this isn't fool proof, + /user/10/ok would get matched here, and we would be assigning + userID to + "10/ok". +

+

And we'll add the new route in our + main function:

+
    func main() {
+        http.HandleFunc("/hello", helloHTTP)
+        http.HandleFunc("/user/", userProfile)
+        http.HandleFunc("/", notFound)
+        log.Fatal(http.ListenAndServe(":8080", nil))
+    }
+
+

+ Note: that this pattern + /user/ matches the trailing + / so that a call to + /user in the browser would return a + 404 Not Found. +

+

Step 5

+

Ok, so we have introduced some pretty severe holes in the security of our new HTTP router. As mentioned in a note above, + treating the + req.URL.Path as a byte slice and just taking the last half is a terrible idea. So let's fix this:

+
    var validPath = regexp.MustCompile("^/(user)/([0-9]+)$")
+
+    func getID(w http.ResponseWriter, req *http.Request) (string, error) {
+        m := validPath.FindStringSubmatch(req.URL.Path)
+        if m == nil {
+            http.NotFound(w, req)
+            return "", errors.New("Invalid ID")
+        }
+        return m[2], nil // The ID is the second subexpression.
+    }
+
+

Now we can use this method in our code:

+
    func userProfile(w http.ResponseWriter, req *http.Request) {
+        userID, err := getID(w, req)
+        if err != nil {
+            return
+        }
+        fmt.Fprintf(w, "User Profile: %q", userID)
+    }
+
+

Conclusion

+

For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more as time allows. + Additionally, I'd like to create a more advanced article that discusses the ability to respond to POST, PUT, and DELETE + requests. Look for an "Advanced HTTP routing in Golang" article in the future. Thanks for reading this far. + I wish you well in your Go endeavors.

\ No newline at end of file diff --git a/posts/basic-http-routing-in-golang.md b/posts/basic-http-routing-in-golang.md index 172576d..d725eec 100644 --- a/posts/basic-http-routing-in-golang.md +++ b/posts/basic-http-routing-in-golang.md @@ -48,7 +48,7 @@ Which basically means, `http.HandleFunc("/url", routingFunction)` where `routing With `fmt.Fprint()` we can pass an `http.ResponseWriter` and a message to display. Our browser will now look like this when we visit the `/url` endpoint. -![](step2-browser-output.png) +![Browser Output for Step 2 - Hellp HTTP](step2-browser-output.png) Here is what `main.go` looks like at this point: @@ -61,11 +61,98 @@ Here is what `main.go` looks like at this point: ) func main() { - http.HandleFunc("/hello", HelloHTTP) + http.HandleFunc("/hello", helloHTTP) log.Fatal(http.ListenAndServe(":8080", nil)) } - func HelloHTTP(w http.ResponseWriter, req *http.Request) { + func helloHTTP(w http.ResponseWriter, req *http.Request) { fmt.Fprint(w, "Hello HTTP") } +Now we could stop there, as this is a "basic" http routing example, but I think it isn't quite useful as an example yet, until we start to see something slightly more practical. + +### Step 3 + +For starters, let's add a `NotFound` page when we don't match a pattern in `HandleFunc`. + +Here is what `main.go` looks like after that: + + package main + + import ( + "fmt" + "log" + "net/http" + ) + + func main() { + http.HandleFunc("/hello", helloHTTP) + http.HandleFunc("/", notFound) + log.Fatal(http.ListenAndServe(":8080", nil)) + } + + func helloHTTP(w http.ResponseWriter, req *http.Request) { + fmt.Fprint(w, "Hello HTTP") + } + + func notFound(w http.ResponseWriter, req *http.Request) { + http.NotFound(w, req) + } + +This will match `/hello` and use the `HelloHTTP` method to print "Hello HTTP" to the browser. Any other urls will get caught by the `/` pattern and be given the `http.NotFound` response to the browser. + +So that works, but I think we can go further. + +### Step 4 + +We need to give ourselves something more specific than the simple contrived `/hello` endpoint above. So let's assume we are needing to get a user profile and we will use the url `/user/:id` where `:id` is an identifier used to get the user profile from our persistance layer (i.e. our database). + +We'll start by creating a new method for this GET request called `userProfile`: + + func userProfile(w http.ResponseWriter, req *http.Request) { + userID := req.URL.Path[len("/user/"):] + fmt.Fprintf(w, "User Profile: %q", userID) + } + +Notice that we get the URL from the `req` variable and we treat the string returned from `req.URL.Path` as a byte slice to get everything after the `/user/` in the string. **Note: this isn't fool proof, `/user/10/ok` would get matched here, and we would be assigning `userID` to `"10/ok"`.** + +And we'll add the new route in our `main` function: + + func main() { + http.HandleFunc("/hello", helloHTTP) + http.HandleFunc("/user/", userProfile) + http.HandleFunc("/", notFound) + log.Fatal(http.ListenAndServe(":8080", nil)) + } + +_Note: that this pattern `/user/` matches the trailing `/` so that a call to `/user` in the browser would return a `404 Not Found`._ + + +### Step 5 + +Ok, so we have introduced some pretty severe holes in the security of our new HTTP router. As mentioned in a note above, treating the `req.URL.Path` as a byte slice and just taking the last half is a terrible idea. So let's fix this: + + var validPath = regexp.MustCompile("^/(user)/([0-9]+)$") + + func getID(w http.ResponseWriter, req *http.Request) (string, error) { + m := validPath.FindStringSubmatch(req.URL.Path) + if m == nil { + http.NotFound(w, req) + return "", errors.New("Invalid ID") + } + return m[2], nil // The ID is the second subexpression. + } + +Now we can use this method in our code: + + func userProfile(w http.ResponseWriter, req *http.Request) { + userID, err := getID(w, req) + if err != nil { + return + } + fmt.Fprintf(w, "User Profile: %q", userID) + } + +## Conclusion + +For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more as time allows. Additionally, I'd like to create a more advanced article that discusses the ability to respond to POST, PUT, and DELETE requests. Look for an "Advanced HTTP routing in Golang" article in the future. Thanks for reading this far. I wish you well in your Go endevors. \ No newline at end of file From 553f5c7762fb88197b51ec5b0a88677e1c5cc4c6 Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 16:15:08 -0500 Subject: [PATCH 5/6] Updated article --- posts/basic-http-routing-in-golang.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/posts/basic-http-routing-in-golang.md b/posts/basic-http-routing-in-golang.md index d725eec..4fe8141 100644 --- a/posts/basic-http-routing-in-golang.md +++ b/posts/basic-http-routing-in-golang.md @@ -12,6 +12,8 @@ I assume you have basic knowledge of the Go language at this point, so if not, i ## Let's begin +The accompanying repo for the code produced in this article is located [on github](https://github.com/leothelocust/basic-http-routing-in-golang). + ### Step 1 Here is our basic folder structure for this basic http routing example: @@ -155,4 +157,4 @@ Now we can use this method in our code: ## Conclusion -For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more as time allows. Additionally, I'd like to create a more advanced article that discusses the ability to respond to POST, PUT, and DELETE requests. Look for an "Advanced HTTP routing in Golang" article in the future. Thanks for reading this far. I wish you well in your Go endevors. \ No newline at end of file +For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more as time allows. Additionally, I'd like to create a more advanced article that discusses the ability to respond to POST, PUT, and DELETE requests. Look for an "Advanced HTTP routing in Golang" article in the future. Thanks for reading this far. I wish you well in your Go endeavors. \ No newline at end of file From 2b22c6c1095375d0b96639824ab2807cb63e5c40 Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Wed, 9 May 2018 16:31:16 -0500 Subject: [PATCH 6/6] Faster build on gitlab; tmp rm travic.yml until switch --- .gitlab-ci.yml | 4 +--- .travis.yml | 10 ---------- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 .travis.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d172ece..fd82049 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,9 +11,6 @@ stages: - test - deploy -before_script: - - apt-get update -qq && apt-get install -y -qq sshpass - test_coverage: stage: test script: @@ -25,6 +22,7 @@ deploy_prod: stage: deploy script: - echo "Deploying to Production" + - apt-get update -qq && apt-get install -y -qq sshpass - npm install - sshpass -V - export SSHPASS=$SSH_PASS diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5e9d953..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -node_js: - - "node" - -cache: - directories: - - "node_modules" - -before_install: - - npm i -g mocha nyc \ No newline at end of file