263 lines
5.6 KiB
Go
263 lines
5.6 KiB
Go
package restful
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/microcosm-cc/bluemonday"
|
|
)
|
|
|
|
// var seededRand *rand.Rand
|
|
var sanitizePolicy *bluemonday.Policy
|
|
|
|
var jsonInvalidMethod = map[string]interface{}{
|
|
"errcode": -1,
|
|
"message": "Invalid method",
|
|
}
|
|
var jsonInvalidData = map[string]interface{}{
|
|
"errcode": -2,
|
|
"message": "Invalid Data",
|
|
}
|
|
|
|
var jsonInvalidID = map[string]interface{}{
|
|
"errcode": -3,
|
|
"message": "Invalid ID",
|
|
}
|
|
|
|
var jsonOPError = map[string]interface{}{
|
|
"errcode": -4,
|
|
"message": "Operation Error",
|
|
}
|
|
|
|
var jsonOK = map[string]interface{}{
|
|
"message": "OK",
|
|
}
|
|
|
|
func init() {
|
|
// seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
sanitizePolicy = bluemonday.UGCPolicy()
|
|
}
|
|
|
|
//Handler - http handler for a restful endpoint
|
|
type Handler struct {
|
|
Name string /*Endpoint name*/
|
|
Model interface{}
|
|
Filter func(*http.Request, *map[string]interface{}) bool
|
|
}
|
|
|
|
//NewHandler - create a new instance of RestfulHandler
|
|
func NewHandler(name string, model interface{}) *Handler {
|
|
handler := &Handler{
|
|
Name: name,
|
|
Model: model,
|
|
}
|
|
return handler
|
|
}
|
|
|
|
//SetFilter - set filter
|
|
func (h *Handler) SetFilter(filter func(*http.Request, *map[string]interface{}) bool) *Handler {
|
|
if filter != nil {
|
|
h.Filter = filter
|
|
}
|
|
return h
|
|
}
|
|
|
|
//sanitize parameters
|
|
func sanitize(params *url.Values) {
|
|
for key := range *params {
|
|
(*params).Set(key, sanitizePolicy.Sanitize((*params).Get(key)))
|
|
}
|
|
}
|
|
|
|
func trimURIPrefix(uri string, stopTag string) []string {
|
|
params := strings.Split(strings.TrimPrefix(strings.TrimSuffix(uri, "/"), "/"), "/")
|
|
last := len(params) - 1
|
|
for i := last; i >= 0; i-- {
|
|
if params[i] == stopTag {
|
|
return params[i+1:]
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
|
|
func parseID(s string) int64 {
|
|
id, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
return id
|
|
}
|
|
|
|
func (h *Handler) httpGet(w http.ResponseWriter, r *http.Request, id int64) {
|
|
m, ok := h.Model.(Querier)
|
|
if !ok {
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
return
|
|
}
|
|
if id != -1 {
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "one",
|
|
"payload": m.FindByID(id),
|
|
})
|
|
return
|
|
}
|
|
query := r.URL.Query()
|
|
sanitize(&query)
|
|
total, records := m.Find(query)
|
|
if h.Filter == nil {
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "query",
|
|
"total": total,
|
|
"payload": records,
|
|
})
|
|
return
|
|
}
|
|
finalRecords := make([]*map[string]interface{}, 0, len(records))
|
|
for _, record := range records {
|
|
if !h.Filter(r, record) {
|
|
finalRecords = append(finalRecords, record)
|
|
}
|
|
}
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "query",
|
|
"total": len(finalRecords),
|
|
"payload": finalRecords,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) httpPost(w http.ResponseWriter, r *http.Request, id int64) {
|
|
m, ok := h.Model.(Inserter)
|
|
if !ok {
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
return
|
|
}
|
|
if err := r.ParseForm(); err != nil {
|
|
log.Println("[ERR] - [RestfulHandler][POST][ParseForm] err:", err)
|
|
outputGzipJSON(w, jsonInvalidData)
|
|
return
|
|
}
|
|
sanitize(&r.PostForm)
|
|
newID, err := m.Insert(r.PostForm)
|
|
if err != nil {
|
|
log.Println("[ERR] - [RestfulHandler][POST] err:", err)
|
|
outputGzipJSON(w, jsonOPError)
|
|
return
|
|
}
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "insert",
|
|
"id": newID,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) httpPut(w http.ResponseWriter, r *http.Request, id int64) {
|
|
if err := r.ParseForm(); err != nil {
|
|
log.Println("[ERR] - [RestfulHandler][PUT][ParseForm] err:", err)
|
|
outputGzipJSON(w, jsonInvalidData)
|
|
return
|
|
}
|
|
sanitize(&r.PostForm)
|
|
|
|
switch id {
|
|
case -1 /*update by query condition*/ :
|
|
// m, ok := h.Model.(Updater)
|
|
// if !ok {
|
|
// outputGzipJSON(w, jsonInvalidMethod)
|
|
// return
|
|
// }
|
|
// query := r.URL.Query()
|
|
// sanitize(&query)
|
|
// rowsAffected, err := m.Update(r.PostForm, query)
|
|
// if err != nil {
|
|
// log.Println("[ERR] - [RestfulHandler][PUT-Update] err:", err)
|
|
// outputGzipJSON(w, jsonOPError)
|
|
// return
|
|
// }
|
|
// outputGzipJSON(w, map[string]interface{}{
|
|
// "message": "ok",
|
|
// "method": "update",
|
|
// "count": rowsAffected,
|
|
// })
|
|
outputGzipJSON(w, jsonInvalidID)
|
|
return
|
|
default /*update by ID*/ :
|
|
m, ok := h.Model.(Setter)
|
|
if !ok {
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
return
|
|
}
|
|
if err := m.Set(id, r.PostForm); err != nil {
|
|
log.Println("[ERR] - [RestfulHandler][PUT-Set] err:", err)
|
|
outputGzipJSON(w, jsonOPError)
|
|
return
|
|
}
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "set",
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
func (h *Handler) httpDelete(w http.ResponseWriter, r *http.Request, id int64) {
|
|
m, ok := h.Model.(Deleter)
|
|
if !ok {
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
return
|
|
}
|
|
switch id {
|
|
case -1:
|
|
outputGzipJSON(w, jsonInvalidID)
|
|
return
|
|
}
|
|
rowsAffected, err := m.Delete(id)
|
|
if err != nil {
|
|
log.Println("[ERR] - [RestfulHandler][DELETE] err:", err)
|
|
outputGzipJSON(w, jsonOPError)
|
|
return
|
|
}
|
|
outputGzipJSON(w, map[string]interface{}{
|
|
"message": "ok",
|
|
"method": "delete",
|
|
"count": rowsAffected,
|
|
})
|
|
}
|
|
|
|
//ServeHTTP - implementation of http.handler
|
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
if h.Model == nil {
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
return
|
|
}
|
|
if DEBUG {
|
|
log.Println("[DEBUG] - [r.RequestURI]:", r.RequestURI)
|
|
}
|
|
params := trimURIPrefix(r.RequestURI, h.Name)
|
|
var id int64 = -1
|
|
if len(params) > 0 {
|
|
id = parseID(sanitizePolicy.Sanitize(params[0]))
|
|
}
|
|
switch r.Method {
|
|
case "GET":
|
|
h.httpGet(w, r, id)
|
|
return
|
|
case "POST":
|
|
h.httpPost(w, r, id)
|
|
return
|
|
case "PUT":
|
|
h.httpPut(w, r, id)
|
|
return
|
|
case "DELETE":
|
|
h.httpDelete(w, r, id)
|
|
return
|
|
default:
|
|
outputGzipJSON(w, jsonInvalidMethod)
|
|
}
|
|
}
|