Add helper function to format JSON responses

Every time a JSON object is returned in an HTTP response, the
appropriate header needs to be set and the object itself needs to be
encoded. Doing this in every function is repetitive and error prone
(getDBFile and postDBScan, for instance, never set any headers).

This adds a helper function to centralize the appropriate JSON response
handling.
This commit is contained in:
Anderson Mesquita 2015-12-15 16:40:38 -05:00 committed by Jakob Borg
parent f208e6f0b6
commit 316be5ee34

View File

@ -130,6 +130,11 @@ func (s *apiSvc) getListener(guiCfg config.GUIConfiguration) (net.Listener, erro
return listener, nil return listener, nil
} }
func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(jsonObject)
}
func (s *apiSvc) Serve() { func (s *apiSvc) Serve() {
s.stop = make(chan struct{}) s.stop = make(chan struct{})
@ -366,15 +371,11 @@ func withDetailsMiddleware(id protocol.DeviceID, h http.Handler) http.Handler {
} }
func (s *apiSvc) restPing(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) restPing(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, map[string]string{"ping": "pong"})
json.NewEncoder(w).Encode(map[string]string{
"ping": "pong",
})
} }
func (s *apiSvc) getSystemVersion(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemVersion(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, map[string]string{
json.NewEncoder(w).Encode(map[string]string{
"version": Version, "version": Version,
"codename": Codename, "codename": Codename,
"longVersion": LongVersion, "longVersion": LongVersion,
@ -384,11 +385,10 @@ func (s *apiSvc) getSystemVersion(w http.ResponseWriter, r *http.Request) {
} }
func (s *apiSvc) getSystemDebug(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemDebug(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
names := l.Facilities() names := l.Facilities()
enabled := l.FacilityDebugging() enabled := l.FacilityDebugging()
sort.Strings(enabled) sort.Strings(enabled)
json.NewEncoder(w).Encode(map[string]interface{}{ sendJSON(w, map[string]interface{}{
"facilities": names, "facilities": names,
"enabled": enabled, "enabled": enabled,
}) })
@ -424,11 +424,7 @@ func (s *apiSvc) getDBBrowse(w http.ResponseWriter, r *http.Request) {
levels = -1 levels = -1
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, s.model.GlobalDirectoryTree(folder, prefix, levels, dirsonly))
tree := s.model.GlobalDirectoryTree(folder, prefix, levels, dirsonly)
json.NewEncoder(w).Encode(tree)
} }
func (s *apiSvc) getDBCompletion(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDBCompletion(w http.ResponseWriter, r *http.Request) {
@ -442,20 +438,15 @@ func (s *apiSvc) getDBCompletion(w http.ResponseWriter, r *http.Request) {
return return
} }
res := map[string]float64{ sendJSON(w, map[string]float64{
"completion": s.model.Completion(device, folder), "completion": s.model.Completion(device, folder),
} })
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getDBStatus(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDBStatus(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query() qs := r.URL.Query()
folder := qs.Get("folder") folder := qs.Get("folder")
res := folderSummary(s.cfg, s.model, folder) sendJSON(w, folderSummary(s.cfg, s.model, folder))
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(res)
} }
func folderSummary(cfg *config.Wrapper, m *model.Model, folder string) map[string]interface{} { func folderSummary(cfg *config.Wrapper, m *model.Model, folder string) map[string]interface{} {
@ -520,35 +511,26 @@ func (s *apiSvc) getDBNeed(w http.ResponseWriter, r *http.Request) {
progress, queued, rest, total := s.model.NeedFolderFiles(folder, page, perpage) progress, queued, rest, total := s.model.NeedFolderFiles(folder, page, perpage)
// Convert the struct to a more loose structure, and inject the size. // Convert the struct to a more loose structure, and inject the size.
output := map[string]interface{}{ sendJSON(w, map[string]interface{}{
"progress": s.toNeedSlice(progress), "progress": s.toNeedSlice(progress),
"queued": s.toNeedSlice(queued), "queued": s.toNeedSlice(queued),
"rest": s.toNeedSlice(rest), "rest": s.toNeedSlice(rest),
"total": total, "total": total,
"page": page, "page": page,
"perpage": perpage, "perpage": perpage,
} })
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(output)
} }
func (s *apiSvc) getSystemConnections(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemConnections(w http.ResponseWriter, r *http.Request) {
var res = s.model.ConnectionStats() sendJSON(w, s.model.ConnectionStats())
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getDeviceStats(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDeviceStats(w http.ResponseWriter, r *http.Request) {
var res = s.model.DeviceStatistics() sendJSON(w, s.model.DeviceStatistics())
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getFolderStats(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getFolderStats(w http.ResponseWriter, r *http.Request) {
var res = s.model.FolderStatistics() sendJSON(w, s.model.FolderStatistics())
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getDBFile(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDBFile(w http.ResponseWriter, r *http.Request) {
@ -559,7 +541,7 @@ func (s *apiSvc) getDBFile(w http.ResponseWriter, r *http.Request) {
lf, _ := s.model.CurrentFolderFile(folder, file) lf, _ := s.model.CurrentFolderFile(folder, file)
av := s.model.Availability(folder, file) av := s.model.Availability(folder, file)
json.NewEncoder(w).Encode(map[string]interface{}{ sendJSON(w, map[string]interface{}{
"global": jsonFileInfo(gf), "global": jsonFileInfo(gf),
"local": jsonFileInfo(lf), "local": jsonFileInfo(lf),
"availability": av, "availability": av,
@ -567,8 +549,7 @@ func (s *apiSvc) getDBFile(w http.ResponseWriter, r *http.Request) {
} }
func (s *apiSvc) getSystemConfig(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemConfig(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, s.cfg.Raw())
json.NewEncoder(w).Encode(s.cfg.Raw())
} }
func (s *apiSvc) postSystemConfig(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) postSystemConfig(w http.ResponseWriter, r *http.Request) {
@ -615,8 +596,7 @@ func (s *apiSvc) postSystemConfig(w http.ResponseWriter, r *http.Request) {
} }
func (s *apiSvc) getSystemConfigInsync(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemConfigInsync(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, map[string]bool{"configInSync": configInSync})
json.NewEncoder(w).Encode(map[string]bool{"configInSync": configInSync})
} }
func (s *apiSvc) postSystemRestart(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) postSystemRestart(w http.ResponseWriter, r *http.Request) {
@ -711,13 +691,11 @@ func (s *apiSvc) getSystemStatus(w http.ResponseWriter, r *http.Request) {
res["uptime"] = int(time.Since(startTime).Seconds()) res["uptime"] = int(time.Since(startTime).Seconds())
res["startTime"] = startTime res["startTime"] = startTime
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, res)
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getSystemError(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemError(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, map[string][]logger.Line{
json.NewEncoder(w).Encode(map[string][]logger.Line{
"errors": s.guiErrors.Since(time.Time{}), "errors": s.guiErrors.Since(time.Time{}),
}) })
} }
@ -736,9 +714,7 @@ func (s *apiSvc) getSystemLog(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query() q := r.URL.Query()
since, err := time.Parse(time.RFC3339, q.Get("since")) since, err := time.Parse(time.RFC3339, q.Get("since"))
l.Debugln(err) l.Debugln(err)
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, map[string][]logger.Line{
json.NewEncoder(w).Encode(map[string][]logger.Line{
"messages": s.systemLog.Since(since), "messages": s.systemLog.Since(since),
}) })
} }
@ -775,7 +751,6 @@ func (s *apiSvc) getSystemHTTPMetrics(w http.ResponseWriter, r *http.Request) {
} }
func (s *apiSvc) getSystemDiscovery(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemDiscovery(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
devices := make(map[string]discover.CacheEntry) devices := make(map[string]discover.CacheEntry)
if s.discoverer != nil { if s.discoverer != nil {
@ -787,17 +762,15 @@ func (s *apiSvc) getSystemDiscovery(w http.ResponseWriter, r *http.Request) {
} }
} }
json.NewEncoder(w).Encode(devices) sendJSON(w, devices)
} }
func (s *apiSvc) getReport(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getReport(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, reportData(s.cfg, s.model))
json.NewEncoder(w).Encode(reportData(s.cfg, s.model))
} }
func (s *apiSvc) getDBIgnores(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDBIgnores(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query() qs := r.URL.Query()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
ignores, patterns, err := s.model.GetIgnores(qs.Get("folder")) ignores, patterns, err := s.model.GetIgnores(qs.Get("folder"))
if err != nil { if err != nil {
@ -805,7 +778,7 @@ func (s *apiSvc) getDBIgnores(w http.ResponseWriter, r *http.Request) {
return return
} }
json.NewEncoder(w).Encode(map[string][]string{ sendJSON(w, map[string][]string{
"ignore": ignores, "ignore": ignores,
"patterns": patterns, "patterns": patterns,
}) })
@ -841,8 +814,6 @@ func (s *apiSvc) getEvents(w http.ResponseWriter, r *http.Request) {
s.fss.gotEventRequest() s.fss.gotEventRequest()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
// Flush before blocking, to indicate that we've received the request // Flush before blocking, to indicate that we've received the request
// and that it should not be retried. // and that it should not be retried.
f := w.(http.Flusher) f := w.(http.Flusher)
@ -853,7 +824,7 @@ func (s *apiSvc) getEvents(w http.ResponseWriter, r *http.Request) {
evs = evs[len(evs)-limit:] evs = evs[len(evs)-limit:]
} }
json.NewEncoder(w).Encode(evs) sendJSON(w, evs)
} }
func (s *apiSvc) getSystemUpgrade(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemUpgrade(w http.ResponseWriter, r *http.Request) {
@ -872,21 +843,20 @@ func (s *apiSvc) getSystemUpgrade(w http.ResponseWriter, r *http.Request) {
res["newer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.Newer res["newer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.Newer
res["majorNewer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.MajorNewer res["majorNewer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.MajorNewer
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, res)
json.NewEncoder(w).Encode(res)
} }
func (s *apiSvc) getDeviceID(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getDeviceID(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query() qs := r.URL.Query()
idStr := qs.Get("id") idStr := qs.Get("id")
id, err := protocol.DeviceIDFromString(idStr) id, err := protocol.DeviceIDFromString(idStr)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if err == nil { if err == nil {
json.NewEncoder(w).Encode(map[string]string{ sendJSON(w, map[string]string{
"id": id.String(), "id": id.String(),
}) })
} else { } else {
json.NewEncoder(w).Encode(map[string]string{ sendJSON(w, map[string]string{
"error": err.Error(), "error": err.Error(),
}) })
} }
@ -899,8 +869,7 @@ func (s *apiSvc) getLang(w http.ResponseWriter, r *http.Request) {
parts := strings.SplitN(l, ";", 2) parts := strings.SplitN(l, ";", 2)
langs = append(langs, strings.ToLower(strings.TrimSpace(parts[0]))) langs = append(langs, strings.ToLower(strings.TrimSpace(parts[0])))
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, langs)
json.NewEncoder(w).Encode(langs)
} }
func (s *apiSvc) postSystemUpgrade(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) postSystemUpgrade(w http.ResponseWriter, r *http.Request) {
@ -971,7 +940,7 @@ func (s *apiSvc) postDBScan(w http.ResponseWriter, r *http.Request) {
errors := s.model.ScanFolders() errors := s.model.ScanFolders()
if len(errors) > 0 { if len(errors) > 0 {
http.Error(w, "Error scanning folders", 500) http.Error(w, "Error scanning folders", 500)
json.NewEncoder(w).Encode(errors) sendJSON(w, errors)
return return
} }
} }
@ -1019,12 +988,10 @@ func (s *apiSvc) getPeerCompletion(w http.ResponseWriter, r *http.Request) {
comp[device] = int(tot[device] / count[device]) comp[device] = int(tot[device] / count[device])
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8") sendJSON(w, comp)
json.NewEncoder(w).Encode(comp)
} }
func (s *apiSvc) getSystemBrowse(w http.ResponseWriter, r *http.Request) { func (s *apiSvc) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
qs := r.URL.Query() qs := r.URL.Query()
current := qs.Get("current") current := qs.Get("current")
search, _ := osutil.ExpandTilde(current) search, _ := osutil.ExpandTilde(current)
@ -1043,7 +1010,8 @@ func (s *apiSvc) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
} }
} }
} }
json.NewEncoder(w).Encode(ret)
sendJSON(w, ret)
} }
type embeddedStatic struct { type embeddedStatic struct {