Last active
January 17, 2025 21:20
-
-
Save nmerouze/2e26a02d23c4c62173fd to your computer and use it in GitHub Desktop.
JSON-API with Go and MongoDB: Final Part
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"log" | |
"net/http" | |
"reflect" | |
"time" | |
"github.com/gorilla/context" | |
"github.com/julienschmidt/httprouter" | |
"github.com/justinas/alice" | |
"gopkg.in/mgo.v2" | |
"gopkg.in/mgo.v2/bson" | |
) | |
// Repo | |
type Tea struct { | |
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"` | |
Name string `json:"name"` | |
Category string `json:"category"` | |
} | |
type TeasCollection struct { | |
Data []Tea `json:"data"` | |
} | |
type TeaResource struct { | |
Data Tea `json:"data"` | |
} | |
type TeaRepo struct { | |
coll *mgo.Collection | |
} | |
func (r *TeaRepo) All() (TeasCollection, error) { | |
result := TeasCollection{[]Tea{}} | |
err := r.coll.Find(nil).All(&result.Data) | |
if err != nil { | |
return result, err | |
} | |
return result, nil | |
} | |
func (r *TeaRepo) Find(id string) (TeaResource, error) { | |
result := TeaResource{} | |
err := r.coll.FindId(bson.ObjectIdHex(id)).One(&result.Data) | |
if err != nil { | |
return result, err | |
} | |
return result, nil | |
} | |
func (r *TeaRepo) Create(tea *Tea) error { | |
id := bson.NewObjectId() | |
_, err := r.coll.UpsertId(id, tea) | |
if err != nil { | |
return err | |
} | |
tea.Id = id | |
return nil | |
} | |
func (r *TeaRepo) Update(tea *Tea) error { | |
err := r.coll.UpdateId(tea.Id, tea) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
func (r *TeaRepo) Delete(id string) error { | |
err := r.coll.RemoveId(bson.ObjectIdHex(id)) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
// Errors | |
type Errors struct { | |
Errors []*Error `json:"errors"` | |
} | |
type Error struct { | |
Id string `json:"id"` | |
Status int `json:"status"` | |
Title string `json:"title"` | |
Detail string `json:"detail"` | |
} | |
func WriteError(w http.ResponseWriter, err *Error) { | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
w.WriteHeader(err.Status) | |
json.NewEncoder(w).Encode(Errors{[]*Error{err}}) | |
} | |
var ( | |
ErrBadRequest = &Error{"bad_request", 400, "Bad request", "Request body is not well-formed. It must be JSON."} | |
ErrNotAcceptable = &Error{"not_acceptable", 406, "Not Acceptable", "Accept header must be set to 'application/vnd.api+json'."} | |
ErrUnsupportedMediaType = &Error{"unsupported_media_type", 415, "Unsupported Media Type", "Content-Type header must be set to: 'application/vnd.api+json'."} | |
ErrInternalServer = &Error{"internal_server_error", 500, "Internal Server Error", "Something went wrong."} | |
) | |
// Middlewares | |
func recoverHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
defer func() { | |
if err := recover(); err != nil { | |
log.Printf("panic: %+v", err) | |
WriteError(w, ErrInternalServer) | |
} | |
}() | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func loggingHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
t1 := time.Now() | |
next.ServeHTTP(w, r) | |
t2 := time.Now() | |
log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1)) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func acceptHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
if r.Header.Get("Accept") != "application/vnd.api+json" { | |
WriteError(w, ErrNotAcceptable) | |
return | |
} | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func contentTypeHandler(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
if r.Header.Get("Content-Type") != "application/vnd.api+json" { | |
WriteError(w, ErrUnsupportedMediaType) | |
return | |
} | |
next.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func bodyHandler(v interface{}) func(http.Handler) http.Handler { | |
t := reflect.TypeOf(v) | |
m := func(next http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
val := reflect.New(t).Interface() | |
err := json.NewDecoder(r.Body).Decode(val) | |
if err != nil { | |
WriteError(w, ErrBadRequest) | |
return | |
} | |
if next != nil { | |
context.Set(r, "body", val) | |
next.ServeHTTP(w, r) | |
} | |
} | |
return http.HandlerFunc(fn) | |
} | |
return m | |
} | |
// Main handlers | |
type appContext struct { | |
db *mgo.Database | |
} | |
func (c *appContext) teasHandler(w http.ResponseWriter, r *http.Request) { | |
repo := TeaRepo{c.db.C("teas")} | |
teas, err := repo.All() | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
json.NewEncoder(w).Encode(teas) | |
} | |
func (c *appContext) teaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
repo := TeaRepo{c.db.C("teas")} | |
tea, err := repo.Find(params.ByName("id")) | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
json.NewEncoder(w).Encode(tea) | |
} | |
func (c *appContext) createTeaHandler(w http.ResponseWriter, r *http.Request) { | |
body := context.Get(r, "body").(*TeaResource) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Create(&body.Data) | |
if err != nil { | |
panic(err) | |
} | |
w.Header().Set("Content-Type", "application/vnd.api+json") | |
w.WriteHeader(201) | |
json.NewEncoder(w).Encode(body) | |
} | |
func (c *appContext) updateTeaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
body := context.Get(r, "body").(*TeaResource) | |
body.Data.Id = bson.ObjectIdHex(params.ByName("id")) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Update(&body.Data) | |
if err != nil { | |
panic(err) | |
} | |
w.WriteHeader(204) | |
w.Write([]byte("\n")) | |
} | |
func (c *appContext) deleteTeaHandler(w http.ResponseWriter, r *http.Request) { | |
params := context.Get(r, "params").(httprouter.Params) | |
repo := TeaRepo{c.db.C("teas")} | |
err := repo.Delete(params.ByName("id")) | |
if err != nil { | |
panic(err) | |
} | |
w.WriteHeader(204) | |
w.Write([]byte("\n")) | |
} | |
// Router | |
type router struct { | |
*httprouter.Router | |
} | |
func (r *router) Get(path string, handler http.Handler) { | |
r.GET(path, wrapHandler(handler)) | |
} | |
func (r *router) Post(path string, handler http.Handler) { | |
r.POST(path, wrapHandler(handler)) | |
} | |
func (r *router) Put(path string, handler http.Handler) { | |
r.PUT(path, wrapHandler(handler)) | |
} | |
func (r *router) Delete(path string, handler http.Handler) { | |
r.DELETE(path, wrapHandler(handler)) | |
} | |
func NewRouter() *router { | |
return &router{httprouter.New()} | |
} | |
func wrapHandler(h http.Handler) httprouter.Handle { | |
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { | |
context.Set(r, "params", ps) | |
h.ServeHTTP(w, r) | |
} | |
} | |
func main() { | |
session, err := mgo.Dial("localhost") | |
if err != nil { | |
panic(err) | |
} | |
defer session.Close() | |
session.SetMode(mgo.Monotonic, true) | |
appC := appContext{session.DB("test")} | |
commonHandlers := alice.New(context.ClearHandler, loggingHandler, recoverHandler, acceptHandler) | |
router := NewRouter() | |
router.Get("/teas/:id", commonHandlers.ThenFunc(appC.teaHandler)) | |
router.Put("/teas/:id", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.updateTeaHandler)) | |
router.Delete("/teas/:id", commonHandlers.ThenFunc(appC.deleteTeaHandler)) | |
router.Get("/teas", commonHandlers.ThenFunc(appC.teasHandler)) | |
router.Post("/teas", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.createTeaHandler)) | |
http.ListenAndServe(":8080", router) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please help , i have problem when try to save consume api result to mongodb, the code it's error
package main
import (
"encoding/json"
"fmt"
"bytes"
"io/ioutil"
"net/http"
//"mydb"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type Response struct {
StatusCode int
json:"statusCode"
Headers map[string]string
json:"headers"
Body string
json:"body"
}
type Passanger struct {
bson.ObjectId
json:"id" bson:"_id"
Title string
json:"title" bson:"title"
Image string
json:"image" bson:"image"
Price string
json:"price" bson:"price"
Rating string
json:"rating" bson:"rating"
}
//type Passanger struct {
//ID bson.ObjectId
bson:"_id,omitempty"
//Title string
//Image string
//Price string
//Rating string
//}
var (
IsDrop = true
)
type Adapter func(http.Handler) http.Handler
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
func main() {
fmt.Println("Starting the application...")
// req.Header.Set("Authorization", strAuthorization)
}
}
Please correct my code, thanks alot before