Skip to content

Instantly share code, notes, and snippets.

@chilledoj
Last active April 18, 2022 19:27
Show Gist options
  • Select an option

  • Save chilledoj/359241fdb37ef7cb7dee45898cfbec3a to your computer and use it in GitHub Desktop.

Select an option

Save chilledoj/359241fdb37ef7cb7dee45898cfbec3a to your computer and use it in GitHub Desktop.
Go Std Library HTTP Code Snippets
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<link href='https://cdn.jsdelivr.net/npm/css.gg/icons/all.css' rel='stylesheet'>
<title>{{defTitle .Title}}</title>
</head>
<body>
<div class="container is-fullhd">
<nav class="navbar pl-4" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/">SimpleTask</a>
</div>
</nav>
{{template "content" .}}
</div>
</body>
</html>
{{end}}
package main
import (
"flag"
"log"
"net"
)
func main() {
var (
host = flag.String("host", "", "host http address to listen on")
port = flag.String("port", "8000", "port number for http listener")
)
flag.Parse()
addr := net.JoinHostPort(*host, *port)
if err := runHttp(addr); err != nil {
log.Fatal(err)
}
}
package main
import (
"fmt"
"net/http"
)
func runHttp(listenAddr string) error {
s := http.Server{
Addr: listenAddr,
Handler: http.DefaultServeMux,
}
fmt.Printf("Starting HTTP listener at %s\n", listenAddr)
return s.ListenAndServe()
}
/* router/errors.go */
type httpErr struct {
err error
msg string
}
func (he httpErr) Error() string {
return he.err.Error()
}
func ErrJSON(w http.ResponseWriter, err error, code int) {
v, ok := err.(httpErr)
if !ok {
fmt.Printf("An error occurred: %v", err)
http.Error(w, err.Error(), code)
return
}
fmt.Printf("An error occurred: %v", v.err)
JSON(w, map[string]string{
"error": v.msg,
}, code)
}
func extractTaskId(r *http.Request) (string, error) {
if len(r.URL.Path) < 2 {
return "", fmt.Errorf("no task id provided")
}
// We know our subrouter will only provide the id as the root path item.
return strings.Split(r.URL.Path[1:], "/")[0], nil
}
{{define "content"}}
<div class="container">
<section class="hero is-info is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">Tasks</h1>
</div>
</div>
</section>
<div class="block is-flex is-justify-content-flex-end p-4">
<a href="/new" class="button is-primary" >
<span class="icon">
<i class="gg-add"></i>
</span>
<span>Add</span>
</a>
</div>
<div class="block" id="tasks">
<div class="columns is-multiline">
{{range $i , $task := .Tasks}}
<div class="column is-one-third">
<div class="card">
<div class="card-content">
<p class="title">{{$task.title}}</p>
<p>{{$task.details}}</p>
</div>
<footer class="card-footer">
<a href="/edit/{{$task.id}}" class="card-footer-item">Edit</a>
<a href="/delete/{{$task.id}}" class="card-footer-item">Delete</a>
</footer>
</div>
</div>
{{end}}
</div>
</div>
</div>
{{end}}
/* router/json.go */
func JSON(w http.ResponseWriter, data interface{}, code int) {
buf, err := json.Marshal(data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
status := http.StatusOK
if code != 0 {
status = code
}
w.WriteHeader(status)
w.Write(buf)
}
.PHONY: build
build:
go build -o ./bin/mystdhttp ./cmd/.
.PHONY: run
run: build
./bin/mystdhttp
func logMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Pre-work
nw := time.Now()
// Run Handler
handler.ServeHTTP(w, r)
// Post-work
fmt.Printf("%s %s %s %s\n", r.RemoteAddr, r.Method, r.URL, time.Since(nw))
})
}
func recoverMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
fmt.Printf("RECOVERING: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()
handler.ServeHTTP(w, r)
})
}
func NewRouter() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", taskListPage())
return mux
}
var tasks = []map[string]string{
{"id": "task-01", "title": "test", "details": "a test task", "status": "active"},
{"id": "task-02", "title": "Another Test", "details": "# Title\n\n+ Some Stuff to do\n+ Another item\n\nSomething else written here", "status": "active"},
{"id": "task-03", "title": "Test 3", "details": "a test task 3", "status": "active"},
{"id": "task-04", "title": "Test 4", "details": "a test task 4", "status": "active"},
{"id": "task-05", "title": "Test 5", "details": "a test task 5", "status": "active"},
{"id": "task-06", "title": "Test 6", "details": "a test task 6", "status": "pending"},
{"id": "task-07", "title": "Test 7", "details": "a test task 7", "status": "pending"},
{"id": "task-08", "title": "Test 8", "details": "a test task 8", "status": "active"},
{"id": "task-09", "title": "Test 9", "details": "a test task 9", "status": "inactive"},
{"id": "task-10", "title": "Test 10", "details": "a test task 10", "status": "inactive"},
}
func taskListPage() http.HandlerFunc {
files := tmplLayout("./web/templates/index.gohtml")
tmpl := template.Must(template.New("index").Funcs(defaultFuncs).ParseFiles(files...))
return func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" || r.Method != http.MethodGet {
http.NotFound(w, r)
return
}
var buf bytes.Buffer
if err := tmpl.ExecuteTemplate(&buf, "base", map[string]interface{}{
"Tasks": tasks,
}); err != nil {
fmt.Printf("ERR: %v\n", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
io.Copy(w, &buf)
}
}
/* web/templates/_notFound.gohtml */
{{define "content"}}
<div class="container">
<section class="hero is-medium is-warning is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">
Not Found
</h1>
<h2 class="subtitle">
Sorry...we couldn't find that one.
</h2>
</div>
</div>
</section>
<div class="block p-2">
<a href="/" class="button is-info is-outlined">
<span class="icon">
<i class="gg-home"></i>
</span>
<span>Home<span>
</a>
</div>
</div>
{{end}}
func (th *TasksHandler) readTaskHandler(w http.ResponseWriter, r *http.Request) {
taskId, err:= extractTaskId(r)
if err!=nil{
ErrJSON(w, httpErr{err: err, msg: "not found"}, http.StatusNotFound)
return
}
task ,err:= someDB.GetTaskById(taskId)
...
}
type TasksHandler struct {}
func (th *TasksHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
if r.URL.Path == "/" {
th.listTaskHandler(w, r)
return
}
th.readTaskHandler(w, r)
case http.MethodPost:
th.createTaskHandler(w, r)
case http.MethodPut:
th.updateTaskHandler(w, r)
case http.MethodDelete:
th.deleteTaskHandler(w, r)
default:
msg := http.StatusText(http.StatusNotFound)
ErrJSON(w, httpErr{err: fmt.Errorf(msg), msg: msg }, http.StatusNotFound)
}
}
func (th *TasksHandler) listTaskHandler(w http.ResponseWriter, r *http.Request) {
// TODO: List Tasks
}
func (th *TasksHandler) readTaskHandler(w http.ResponseWriter, r *http.Request) {
// TODO: Read Task by ID
}
func (th *TasksHandler) deleteTaskHandler(w http.ResponseWriter, r *http.Request) {
// TODO: Delete Task by ID
}
func (th *TasksHandler) createTaskHandler(w http.ResponseWriter, r *http.Request) {
// TODO: Create Task by ID
}
func (th *TasksHandler) updateTaskHandler(w http.ResponseWriter, r *http.Request) {
// TODO: Update Task by ID
}
/* routes/router.go */
// Change the http.NotFound to notFoundPage(w, r)
func taskListPage() http.HandlerFunc {
...
return func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" || r.Method != http.MethodGet {
notFoundPage(w, r)
return
}
...
}
}
... at the end of the file add this function
var notFoundPage = func() http.HandlerFunc {
files := tmplLayout("./web/templates/_notFound.gohtml")
tmpl := template.Must(template.New("index").Funcs(defaultFuncs).ParseFiles(files...))
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var buf bytes.Buffer
if err := tmpl.ExecuteTemplate(&buf, "base", nil); err != nil {
fmt.Printf("ERR: %v\n", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
io.Copy(w, &buf)
})
}()
package router
import "net/http"
func NewRouter() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
return mux
}
package router
import "html/template"
var defaultFuncs = template.FuncMap{
"defTitle": func(ip interface{}) string {
v, ok := ip.(string)
if !ok || (ok && v == "") {
return "Tasks - Golang Std HTTP"
}
return v
},
}
var templateFiles = []string{
"./web/templates/base.gohtml",
}
func tmplLayout(files ...string) []string {
return append(templateFiles, files...)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment