forked from nathankerr/rest
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver.go
149 lines (137 loc) · 3.67 KB
/
server.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
package rest
import (
"fmt"
"log"
"net/http"
"strings"
)
var resources = make(map[string]interface{})
// Generic resource handler
func resourceHandler(c http.ResponseWriter, req *http.Request) {
uriPath := req.URL.Path
// try to get resource with full uri path
resource, ok := resources[uriPath]
var id string
if !ok {
// no resource found, thus check if the path is a resource + ID
i := strings.LastIndex(uriPath, "/")
if i == -1 {
log.Println("No slash found in URIPath ", uriPath)
NotFound(c)
return
}
// Move index to after slash as that’s where we want to split
i++
id = uriPath[i:]
uriPathParent := uriPath[:i]
resource, ok = resources[uriPathParent]
if !ok {
log.Println("Invalid URIPath-Parent ", uriPathParent)
NotFound(c)
return
}
}
var hasAccess bool = false
if accesschecker, ok := resource.(accessChecker); ok {
hasAccess, _ = accesschecker.HasAccess(req)
} else {
// no checker for resource, so always give access
log.Println("Resource ", uriPath, " has no accessChecker. Giving access …")
hasAccess = true
}
if hasAccess {
// call method on resource corresponding to the HTTP method
switch req.Method {
case "GET":
if len(id) == 0 {
// no ID -> Index
if resIndex, ok := resource.(indexer); ok {
resIndex.Index(c)
} else {
NotImplemented(c)
}
} else {
// Find by ID
if resFind, ok := resource.(finder); ok {
resFind.Find(c, id)
} else {
NotImplemented(c)
}
}
case "POST":
// Create
if resCreate, ok := resource.(creater); ok {
resCreate.Create(c, req)
} else {
NotImplemented(c)
}
case "PUT":
// Update
if resUpdate, ok := resource.(updater); ok {
resUpdate.Update(c, id, req)
} else {
NotImplemented(c)
}
case "DELETE":
// Delete
if resDelete, ok := resource.(deleter); ok {
resDelete.Delete(c, id)
} else {
NotImplemented(c)
}
case "OPTIONS":
// List usable HTTP methods
if resOptions, ok := resource.(optioner); ok {
resOptions.Options(c, id)
} else {
NotImplemented(c)
}
default:
NotImplemented(c)
}
}
return
}
// Add a resource route
func Resource(path string, res interface{}) {
// check and warn for missing leading slash
if fmt.Sprint(path[0:1]) != "/" {
log.Println("Resource was added with a path with no leading slash. Did you mean to add /", path, " ?")
}
// add potentially missing trailing slash (resource always ends with slash)
pathLen := len(path)
if pathLen > 1 && path[pathLen-1:pathLen] != "/" {
log.Println("adding trailing slash to ", path)
path = fmt.Sprint(path, "/")
}
log.Println("Adding resource ", res, " at ", path)
resources[path] = res
http.Handle(path, http.HandlerFunc(resourceHandler))
}
// Emits a 404 Not Found
func NotFound(c http.ResponseWriter) {
http.Error(c, "404 Not Found", http.StatusNotFound)
}
// Emits a 501 Not Implemented
func NotImplemented(c http.ResponseWriter) {
http.Error(c, "501 Not Implemented", http.StatusNotImplemented)
}
// Emits a 201 Created with the URI for the new location
func Created(c http.ResponseWriter, location string) {
c.Header().Set("Location", location)
http.Error(c, "201 Created", http.StatusCreated)
}
// Emits a 200 OK with a location. Used when after a PUT
func Updated(c http.ResponseWriter, location string) {
c.Header().Set("Location", location)
http.Error(c, "200 OK", http.StatusOK)
}
// Emits a bad request with the specified instructions
func BadRequest(c http.ResponseWriter, instructions string) {
c.WriteHeader(http.StatusBadRequest)
c.Write([]byte(instructions))
}
// Emits a 204 No Content
func NoContent(c http.ResponseWriter) {
http.Error(c, "204 No Content", http.StatusNoContent)
}