-
Notifications
You must be signed in to change notification settings - Fork 228
/
main.go
177 lines (151 loc) · 5.8 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Command example is a sample application built with Goji. Its goal is to give
// you a taste for what Goji looks like in the real world by artificially using
// all of its features.
//
// In particular, this is a complete working site for gritter.com, a site where
// users can post 140-character "greets". Any resemblance to real websites,
// alive or dead, is purely coincidental.
package main
import (
"fmt"
"io"
"net/http"
"regexp"
"strconv"
"time"
"github.com/goji/param"
"github.com/zenazn/goji"
"github.com/zenazn/goji/web"
"github.com/zenazn/goji/web/middleware"
)
// Note: the code below cuts a lot of corners to make the example app simple.
func main() {
// Add routes to the global handler
goji.Get("/", Root)
// Fully backwards compatible with net/http's Handlers
goji.Get("/greets", http.RedirectHandler("/", 301))
// Use your favorite HTTP verbs
goji.Post("/greets", NewGreet)
// Use Sinatra-style patterns in your URLs
goji.Get("/users/:name", GetUser)
// Goji also supports regular expressions with named capture groups.
goji.Get(regexp.MustCompile(`^/greets/(?P<id>\d+)$`), GetGreet)
// Middleware can be used to inject behavior into your app. The
// middleware for this application are defined in middleware.go, but you
// can put them wherever you like.
goji.Use(PlainText)
// If the patterns ends with "/*", the path is treated as a prefix, and
// can be used to implement sub-routes.
admin := web.New()
goji.Handle("/admin/*", admin)
// The standard SubRouter middleware helps make writing sub-routers
// easy. Ordinarily, Goji does not manipulate the request's URL.Path,
// meaning you'd have to repeat "/admin/" in each of the following
// routes. This middleware allows you to cut down on the repetition by
// eliminating the shared, already-matched prefix.
admin.Use(middleware.SubRouter)
// You can also easily attach extra middleware to sub-routers that are
// not present on the parent router. This one, for instance, presents a
// password prompt to users of the admin endpoints.
admin.Use(SuperSecure)
admin.Get("/", AdminRoot)
admin.Get("/finances", AdminFinances)
// Goji's routing, like Sinatra's, is exact: no effort is made to
// normalize trailing slashes.
goji.Get("/admin", http.RedirectHandler("/admin/", 301))
// Use a custom 404 handler
goji.NotFound(NotFound)
// Sometimes requests take a long time.
goji.Get("/waitforit", WaitForIt)
// Call Serve() at the bottom of your main() function, and it'll take
// care of everything else for you, including binding to a socket (with
// automatic support for systemd and Einhorn) and supporting graceful
// shutdown on SIGINT. Serve() is appropriate for both development and
// production.
goji.Serve()
}
// Root route (GET "/"). Print a list of greets.
func Root(w http.ResponseWriter, r *http.Request) {
// In the real world you'd probably use a template or something.
io.WriteString(w, "Gritter\n======\n\n")
for i := len(Greets) - 1; i >= 0; i-- {
Greets[i].Write(w)
}
}
// NewGreet creates a new greet (POST "/greets"). Creates a greet and redirects
// you to the created greet.
//
// To post a new greet, try this at a shell:
// $ now=$(date +'%Y-%m-%dT%H:%M:%SZ')
// $ curl -i -d "user=carl&message=Hello+World&time=$now" localhost:8000/greets
func NewGreet(w http.ResponseWriter, r *http.Request) {
var greet Greet
// Parse the POST body into the Greet struct. The format is the same as
// is emitted by (e.g.) jQuery.param.
r.ParseForm()
err := param.Parse(r.Form, &greet)
if err != nil || len(greet.Message) > 140 {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// We make no effort to prevent races against other insertions.
Greets = append(Greets, greet)
url := fmt.Sprintf("/greets/%d", len(Greets)-1)
http.Redirect(w, r, url, http.StatusCreated)
}
// GetUser finds a given user and her greets (GET "/user/:name")
func GetUser(c web.C, w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Gritter\n======\n\n")
handle := c.URLParams["name"]
user, ok := Users[handle]
if !ok {
http.Error(w, http.StatusText(404), 404)
return
}
user.Write(w, handle)
io.WriteString(w, "\nGreets:\n")
for i := len(Greets) - 1; i >= 0; i-- {
if Greets[i].User == handle {
Greets[i].Write(w)
}
}
}
// GetGreet finds a particular greet by ID (GET "/greets/\d+"). Does no bounds
// checking, so will probably panic.
func GetGreet(c web.C, w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(c.URLParams["id"])
if err != nil {
http.Error(w, http.StatusText(404), 404)
return
}
// This will panic if id is too big. Try it out!
greet := Greets[id]
io.WriteString(w, "Gritter\n======\n\n")
greet.Write(w)
}
// WaitForIt is a particularly slow handler (GET "/waitforit"). Try loading this
// endpoint and initiating a graceful shutdown (Ctrl-C) or Einhorn reload. The
// old server will stop accepting new connections and will attempt to kill
// outstanding idle (keep-alive) connections, but will patiently stick around
// for this endpoint to finish. How kind of it!
func WaitForIt(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "This is going to be legend... (wait for it)\n")
if fl, ok := w.(http.Flusher); ok {
fl.Flush()
}
time.Sleep(15 * time.Second)
io.WriteString(w, "...dary! Legendary!\n")
}
// AdminRoot is root (GET "/admin/root"). Much secret. Very administrate. Wow.
func AdminRoot(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Gritter\n======\n\nSuper secret admin page!\n")
}
// AdminFinances would answer the question 'How are we doing?'
// (GET "/admin/finances")
func AdminFinances(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Gritter\n======\n\nWe're broke! :(\n")
}
// NotFound is a 404 handler.
func NotFound(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Umm... have you tried turning it off and on again?", 404)
}