view controllers/json.go @ 257:dfc2b035e055

Slimming down the signature of the JSONHandler type to not take the http.ResponseWriter. Idea of this handler is to simply transform JSON to JSON. The input is already parsed. The output is generated from JSONResult. So there is no need to pass the ResponseWriter to the handler function.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 27 Jul 2018 13:03:56 +0200
parents 3b688fe04c39
children 0777aa6de45b
line wrap: on
line source

package controllers

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/jackc/pgx"

	"gemma.intevation.de/gemma/auth"
)

type JSONResult struct {
	Code   int
	Result interface{}
}

type JSONHandler struct {
	Input  func() interface{}
	Handle func(interface{}, *http.Request, *sql.DB) (JSONResult, error)
}

type JSONError struct {
	Code    int
	Message string
}

func (je JSONError) Error() string {
	return fmt.Sprintf("%d: %s", je.Code, je.Message)
}

func (j *JSONHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

	var input interface{}
	if j.Input != nil {
		input = j.Input()
		defer req.Body.Close()
		if err := json.NewDecoder(req.Body).Decode(input); err != nil {
			http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
			return
		}
	}

	token, _ := auth.GetToken(req)
	var jr JSONResult
	err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) {
		jr, err = j.Handle(input, req, db)
		return err
	})

	if err != nil {
		switch e := err.(type) {
		case pgx.PgError:
			var res = struct {
				Result  string `json:"result"`
				Code    string `json:"code"`
				Message string `json:"message"`
			}{
				Result:  "failure",
				Code:    e.Code,
				Message: e.Message,
			}
			rw.Header().Set("Content-Type", "application/json")
			rw.WriteHeader(http.StatusInternalServerError)
			if err := json.NewEncoder(rw).Encode(&res); err != nil {
				log.Printf("error: %v\n", err)
			}
		case JSONError:
			rw.Header().Set("Content-Type", "application/json")
			rw.WriteHeader(http.StatusInternalServerError)
			var res = struct {
				Message string `json:"message"`
			}{
				Message: e.Message,
			}
			if err := json.NewEncoder(rw).Encode(&res); err != nil {
				log.Printf("error: %v\n", err)
			}
		default:
			log.Printf("err: %v\n", err)
			http.Error(rw,
				"error: "+err.Error(),
				http.StatusInternalServerError)
		}
		return
	}

	if jr.Code == 0 {
		jr.Code = http.StatusOK
	}

	if jr.Code != http.StatusNoContent {
		rw.Header().Set("Content-Type", "application/json")
	}
	rw.WriteHeader(jr.Code)
	if jr.Code != http.StatusNoContent {
		if err := json.NewEncoder(rw).Encode(jr.Result); err != nil {
			log.Printf("error: %v\n", err)
		}
	}
}