Skip to content

Instantly share code, notes, and snippets.

@jiacai2050
Last active August 25, 2020 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jiacai2050/b7445647006237f5fbb27cc2494ad3b0 to your computer and use it in GitHub Desktop.
Save jiacai2050/b7445647006237f5fbb27cc2494ad3b0 to your computer and use it in GitHub Desktop.
A simple web app demonstrate how to deal with err in handler
package main
import (
"bytes"
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
type TodoApp struct {
addr string
// we can add some backend here
// db string
s *http.Server
}
func (a *TodoApp) Close() error {
log.Println("app closed...")
return a.s.Close()
}
func (a *TodoApp) Listen() error {
log.Printf("app start, listen on %s...\n", a.addr)
// maybe we can put handler registration when new TodoApp?
mux := http.NewServeMux()
mux.Handle("/", appHandler(a.list))
mux.Handle("/about", appHandler(a.home))
mux.Handle("/error", appHandler(a.error))
a.s.Handler = mux
go func() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
for {
sig := <-sigs
log.Printf("got close signal: %v", sig)
if err := a.Close(); err != nil {
log.Fatalf("stop all failed: %v", err)
}
}
}()
return a.s.ListenAndServe()
}
func (a *TodoApp) list(resp http.ResponseWriter, req *http.Request) AppResponse {
return SuccessString("this is a new todo list!")
}
func (a *TodoApp) home(resp http.ResponseWriter, req *http.Request) AppResponse {
return SuccessString("a todo app in construction")
}
func (a *TodoApp) error(resp http.ResponseWriter, req *http.Request) AppResponse {
return FailError(errors.New("a demo show how to deal with error"))
}
type AppResponse struct {
// required
status int
// optional
headers map[string]string
// body / err only one can be nil
body io.ReadCloser
err error
}
func SuccessString(body string) AppResponse {
return SuccessReader(ioutil.NopCloser(bytes.NewBufferString(body)))
}
func SuccessReader(r io.ReadCloser) AppResponse {
return AppResponse{
status: 200,
body: r,
}
}
func FailError(e error) AppResponse {
return AppResponse{
status: 500,
err: e,
}
}
type appHandler func(http.ResponseWriter, *http.Request) AppResponse
func (ah appHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
ret := ah(resp, req)
resp.WriteHeader(ret.status)
hs := resp.Header()
for k, v := range ret.headers {
hs.Add(k, v)
}
if ret.err != nil {
if n, err := io.WriteString(resp, ret.err.Error()); err != nil {
log.Printf("write response failed %v, written: %d", err, n)
}
return
}
defer ret.body.Close()
if n, err := io.Copy(resp, ret.body); err != nil {
log.Printf("write response failed %v, written: %d", err, n)
}
}
func main() {
addr := ":11119"
app := &TodoApp{
addr: addr,
s: &http.Server{Addr: addr},
}
if err := app.Listen(); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment