This article just have a purpose to show how to create CRUD (Create, Read, Update & Delete) Apps just using http/net.
First step we need to create project directory.
mkdir golang-basic-http # create directory project
cd golang-basic-http # go to project
go mod init golang-basic-http # initialize go module inside the project
Next, please create file main.go, with touch main.go
inside the project directory.
package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
Than, execute the main.go.
go run main.go
Hello world # the result
Next, we need to import http/net
and create the http server.
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("Hello World"))
})
log.Fatal(http.ListenAndServe(":8000", nil))
}
Ok, http.HandleFunc
have 2 parameters, to fill with route path and handler, path describe the route, and handler is a function, and the function have 2 parameters http.ResponseWriter
to write the output, and http.Request
to catch the request from http.
Next, inside handler we need to create response Hello World
if user access the path.
// rw is http.ResponseWriter
rw.Write([]byte("Hello world"))
And to listens on the TCP network we using http.ListenAndServer
.
http.ListenAndServer(":8000", nil)
http.ListenAndServer
have 2 parameters, first parameter fill with the port address, and next with route config, because we don't use router configuration, for this time, second parameter in http.ListenAndServer
we can fill with nil.
If you access the http with port 8000
using curl in the terminal.
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 04:58:38 GMT
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Hello world%
Yes, congratulations.
Next, we need create methods GET
, POST
, DELETE
, and PUT
inside the handler.
To get methods, we need access r
in http.Request
.
switch r.Method {
case "GET":
rw.Write([]byte("GET/ Hello World"))
case "POST":
rw.Write([]byte("POST/ Hello World"))
}
Try to access GET
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 05:26:01 GMT
Content-Length: 16
Content-Type: text/plain; charset=utf-8
GET/ Hello World%
Next, try to access POST
curl -i -X POST http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 05:25:44 GMT
Content-Length: 17
Content-Type: text/plain; charset=utf-8
POST/ Hello World%
Ok, next we need to define data, example Todo with have Task
and ID
.
type Todo struct {
ID int
Task string
}
func main() {
...
...and, define collection of Todo, in todos variable.
type Todo struct {
ID int
Task string
}
var todos []Todo
func main() {
...
...next we need to access todos, inside handler.
switch r.Method {
case "GET":
json.NewEncoder(rw).Encode(todos)
...
json.NewEncoder(rw).Encode(todos)
convert todos and encode to json format as the result.
Save, and next run go run main.go
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 05:54:23 GMT
Content-Length: 5
Content-Type: text/plain; charset=utf-8
null
Oops, the result is null
cause todos is empty, we need to create post to fill todos with new value.
case "POST":
// create variable to save todo
var todo Todo
// decode `r.Body` `r *http.Request` to get data from request
// decode the result to todo
json.NewDecoder(r.Body).Decode(&todo)
// append new todo in todos
todos = append(todos, todo)
// finally, encode again create the resutl with json format
json.NewEncoder(rw).Encode(todo)
}
...next, save and try again, we need to access POST
to save new value.
curl -i -X POST -d '{"ID": 1, "Task": "Coding..."}' http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:01:37 GMT
Content-Length: 28
Content-Type: text/plain; charset=utf-8
{"ID":1,"Task":"Coding..."}
...next, access GET.
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:01:59 GMT
Content-Length: 30
Content-Type: text/plain; charset=utf-8
[{"ID":1,"Task":"Coding..."}]
Next, we need add method DELETE
to remove todo base on the ID
in todo.
case "DELETE":
// http/net not support path variable like this `/:id`
// and to handle it, we using `r.URL.Query` to get the query url
// http://localhost:8000?id=1
query := r.URL.Query()
// id result have a type string
// we need `strconv.Atoi` to convert string into int
id, _ := strconv.Atoi(query.Get("id"))
// next we need to loop todos, and match the id with todo.ID
for index, todo := range todos {
if todo.ID == id {
// splice and replace the todo
todos = append(todos[:index], todos[index+1:]...)
rw.Write([]byte("Success to deleted todo"))
}
}
...save, and run again
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:25:21 GMT
Content-Length: 30
Content-Type: text/plain; charset=utf-8
[{"ID":1,"Task":"Coding..."}]
curl -i -X DELETE http://localhost:8000\?id\=1
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:26:30 GMT
Content-Length: 23
Content-Type: text/plain; charset=utf-8
Success to deleted todo%
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:27:00 GMT
Content-Length: 3
Content-Type: text/plain; charset=utf-8
[]
case "PUT":
query := r.URL.Query()
id, _ := strconv.Atoi(query.Get("id"))
for index, todo := range todos {
json.NewDecoder(r.Body).Decode(&todo)
if todo.ID == id {
todos[index].ID = todo.ID
todos[index].Task = todo.Task
rw.Write([]byte("Success to update todo"))
}
}
}
...yup, save, and run again
curl -i -X POST -d '{"ID": 1, "Task": "Coding..."}' http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:33:35 GMT
Content-Length: 28
Content-Type: text/plain; charset=utf-8
{"ID":1,"Task":"Coding..."}
---
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:34:10 GMT
Content-Length: 30
Content-Type: text/plain; charset=utf-8
[{"ID":1,"Task":"Coding..."}]
...update data
curl -i -X PUT -d '{"ID": 1, "Task": "Bobo siang"}' http://localhost:8000\?id\=1
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:35:32 GMT
Content-Length: 22
Content-Type: text/plain; charset=utf-8
Success to update todo%
---
curl -i http://localhost:8000
HTTP/1.1 200 OK
Date: Sun, 22 Aug 2021 06:39:24 GMT
Content-Length: 35
Content-Type: text/plain; charset=utf-8
[{"ID": 1, "Task": "Bobo siang"}]
Yes, congratulations!, and next we need to custom header, and need a little refactor.
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
)
type Todo struct {
ID int
Task string
}
var todos []Todo
func main() {
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Add("Content-Type", "application/json")
rw.Header().Add("Access-Control-Allow-Origin", "*")
query := r.URL.Query()
id, _ := strconv.Atoi(query.Get("id"))
switch r.Method {
case "GET":
rw.WriteHeader(http.StatusOK)
json.NewEncoder(rw).Encode(todos)
case "POST":
var todo Todo
json.NewDecoder(r.Body).Decode(&todo)
todos = append(todos, todo)
rw.WriteHeader(http.StatusOK)
json.NewEncoder(rw).Encode(todo)
case "DELETE":
for index, todo := range todos {
if todo.ID == id {
todos = append(todos[:index], todos[index+1:]...)
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"message": "Success to delete todo"}`))
}
}
case "PUT":
for index, todo := range todos {
if todo.ID == id {
json.NewDecoder(r.Body).Decode(&todo)
todos[index].ID = todo.ID
todos[index].Task = todo.Task
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(`{"message": "Success to update todo"}`))
}
}
}
})
log.Fatal(http.ListenAndServe(":8000", nil))
}
curl http://localhost:8000 -v
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Type: application/json
< Date: Sun, 22 Aug 2021 06:54:01 GMT
< Content-Length: 5
<
null
* Connection #0 to host localhost left intact
* Closing connection 0
Yes, hopefully you can learn something with this basic article about http server in golang.
Top comments (1)
hey great article... helps a lot!