Gorm and go-chi REST patch resource - go

I am building a REST API using chi and gorm
I want to have a patch route where I can update only the properties I receive in the request body.
I am not sure how is the best way of passing there properties to gorm update method.
Which would be a good way of doing so?
Here is the handler method.
func (m Env) UpdateHandler(w http.ResponseWriter, r *http.Request) {
var delta InsurancePolicy
insurancePolicy := r.Context().Value("insurancePolicy").(InsurancePolicy)
err := json.NewDecoder(r.Body).Decode(&delta)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
result := m.DB.Model(&insurancePolicy).Update(delta)
if result.Error != nil {
w.WriteHeader(http.StatusInternalServerError)
} else {
err := json.NewEncoder(w).Encode(insurancePolicy)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
}
It uses this middleware to preload the request:
func (m Env) InsurancePolicyCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var insurancePolicy InsurancePolicy
result := m.DB.First(&insurancePolicy, chi.URLParam(r, "id"))
if result.Error != nil {
w.WriteHeader(http.StatusNotFound)
return
}
ctx := context.WithValue(r.Context(), "insurancePolicy", insurancePolicy)
next.ServeHTTP(w, r.WithContext(ctx))
})
}

Related

Golang Fiber and Auth0

I'm new to golang, and have followed this (https://auth0.com/blog/authentication-in-golang/) auth0 guide, for setting up a go rest api.
I'm struggeling with converting to Fiber, and in the same time putting my functions that are being called by routes, out to seperate files.
Currently my main file looks like this:
func main() {
r := mux.NewRouter()
r.Handle("/", http.FileServer(http.Dir("./views/")))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
r.Handle("/posts", config.JwtMiddleware.Handler(GetPosts)).Methods("GET")
//r.Handle("/products/{slug}/feedback", jwtMiddleware.Handler(AddFeedbackHandler)).Methods("POST")
// For dev only - Set up CORS so React client can consume our API
corsWrapper := cors.New(cors.Options{
AllowedMethods: []string{"GET", "POST"},
AllowedHeaders: []string{"Content-Type", "Origin", "Accept", "*"},
})
http.ListenAndServe(":8080", corsWrapper.Handler(r))
}
var GetPosts= http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
collection, err := config.GetMongoDbCollection(dbName, collectionName)
if err != nil {
fmt.Println("Error")
}else{
fmt.Println(collection)
//findOptions := options.Find()
cursor, err := collection.Find(context.Background(), bson.M{})
if err != nil {
log.Fatal(err)
}
var posts[]bson.M
if err = cursor.All(context.Background(), &posts); err != nil {
log.Fatal(err)
}
fmt.Println(posts)
payload, _ := json.Marshal(posts)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(payload))
}
})
So I would like to convert from: r := mux.NewRouter() to fiber and in the same time move my GetPosts function out in a seperate file. When doing this, I can't seem to continue calling my jwtMiddleware.
I have tried this package: https://github.com/Mechse/fiberauth0 but it seems like its broken. At least I can call protected routes without supplying jwt tokens in my header.
You can simply convert 'net/http' style middleware handlers with the provided adaptor package(https://github.com/gofiber/adaptor). Note you need to make some changes to the function signature provided by auth0 but this works;
// EnsureValidToken is a middleware that will check the validity of our JWT.
func ensureValidToken(next http.Handler) http.Handler {
issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
if err != nil {
log.Fatalf("Failed to parse the issuer url: %v", err)
}
provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute)
jwtValidator, err := validator.New(
provider.KeyFunc,
validator.RS256,
issuerURL.String(),
[]string{os.Getenv("AUTH0_AUDIENCE")},
validator.WithCustomClaims(
func() validator.CustomClaims {
return &CustomClaims{}
},
),
validator.WithAllowedClockSkew(time.Minute),
)
if err != nil {
log.Fatalf("Failed to set up the jwt validator")
}
errorHandler := func(w http.ResponseWriter, r *http.Request, err error) {
log.Printf("Encountered error while validating JWT: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"message":"Failed to validate JWT."}`))
}
middleware := jwtmiddleware.New(
jwtValidator.ValidateToken,
jwtmiddleware.WithErrorHandler(errorHandler),
)
return middleware.CheckJWT(next)
}
var EnsureValidToken = adaptor.HTTPMiddleware(ensureValidToken)
app := fiber.New()
app.Use(EnsureValidToken)
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")

How to implement http.Hijacker in middleware

I tried to add websocket within my restful web server, but (maybe) because of the middleware block the ws response Hijacker, it cannot build the connection succeed.
// main.go
api := http.Server{
...
Handler: handlers.API(middleware...), // pass the needed middleware.
}
// handler.go
func API(middleware...) http.Handler {
app := web.NewApp(middleware...)
app.Handle(http.MethodGet, "/ws", wsHandler)
}
func wsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
upgrader.CheckOrigin = func(r *http.Request) bool {
return true
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return errors.Wrapf(err, "")
}
defer conn.Close()
for {
messageType, p, err := conn.ReadMessage()
if err != nil {
...
return web.NewRequestError(err, http.StatusInternalServerError)
}
...
}
}
// web.go
type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request) error
type Middleware func(Handler) Handler
func (a *App) Handle(method, path string, handler Handler, mw ...Middleware) {
handler = wrapMiddleware(mw, handler)
...
}
func wrapMiddleware(mw []Middleware, handler Handler) Handler {
for i := len(mw) - 1; i >= 0; i-- {
h := mw[i]
if h != nil {
handler = h(handler)
}
}
return handler
}

Golang http server return html or json depending on content type

I am trying to write simple RESTful app in Golang using gorilla mux.
I wrote few handlers that look as follows:
func getUser(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-type") == "application/json" {
w.Header().Set("Content-Type", "application/json")
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
json.NewEncoder(w).Encode(u) //asked for json, return json
} else {
w.Header().Set("Content-Type", "text/html")
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
renderTemplate(w, "view", u) // asked for html, return html
}
}
func _getUser(r *http.Request) (*User, error) {
params := mux.Vars(r)
for _, u := range users {
if u.ID == params["id"] {
return &u, nil
}
}
return nil, errors.New("")
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/v1/users/{id}", getUser).Methods("GET")
}
The problem I got here is that I have got a lot of duplication. Every CRUD method would have to check for content type and return either json or html.
I thought about writing a closure
func htmlOrJSON(fn func(http.ResponseWriter, *http.Request) (interface {}, error), templateName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-type") == "application/json" {
w.Header().Set("Content-Type", "application/json")
result, err := fn(w, r)
if err != nil {
http.NotFound(w, r)
return
}
json.NewEncoder(w).Encode(result)
} else {
w.Header().Set("Content-Type", "text/html")
result, err := fn(w, r)
if err != nil {
http.NotFound(w, r)
return
}
renderTemplate(w, templateName, result)
}
}
}
// and use as:
router.HandleFunc("/v1/users/{id}", htmlOrJSON(getUser, "view")).Methods("GET")
To remove duplication but it also does not look well. Could anyone help me make this code cleaner?
Although this is a code review question and should be in the CodeReview community, I’ll try to answer it.
Write a generic function that handles HTML and JSON rendering. The error handling IMO should happen on each handler even if you duplicate some code. It makes more sense there and makes the code more readable and explicit. You will soon see that there will be other errors that require special handling.
On the logic, most APIs accept query parameters http://api.com/user/1?fomtat=json. This makes more sense because when a client accept more than content types you will be stuck.
const JSON = "application/json"
func getUser(w http.ResponseWriter, r *http.Request) {
u, err := _getUser(r)
if err != nil {
http.NotFound(w, r)
return
}
responseBody(u, r.Header.Get("Content-type"), &w)
}
func responseBody(u User, contentType string, w io.writer) {
switch contentType {
case JSON:
w.Header().Set("Content-Type", JSON)
json.NewEncoder(w).Encode(u) //asked for json, return json
default:
w.Header().Set("Content-Type", "text/html")
renderTemplate(w, "view", u) // asked for html, return html
}
}

Session middleware for Go?

func indexHandler(w http.ResponseWriter, req *http.Request) {
session, err := store.Get(req, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
}
err = views.ExecuteTemplate(w, "index.html", session.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
All my handlers use Gorilla sessions. How can I avoid store.Getting the session in each of my handlers, i.e. code repeating?
Another issue, is there a better way to give the template the session values, other that an explicit way like:
err = views.ExecuteTemplate(w, "index.html",
struct {
Session map[interface{}]interface{},
...other handler specific data for the template
}{
session.Values,
...
})
Code samples originate from https://github.com/kaihendry/internal-google-login
For the middleware you can define a function that takes a handler as an argument and returns another handler as its result.
func sessionMiddleware(h http.HandlerFunc) http.HandlerFunc {
// ...
}
The returned handler can store.Get the session, if it's not present return the error, if it is present store the session into the request's context and then call the actual handler.
func sessionMiddleware(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
return
}
r = r.WithContext(context.WithValue(r.Context(), "session", session))
h(w, r)
}
}
Now your handlers will still need to "get" the session value from the context however any handler that is wrapped by sessionMiddleware can assume, when it's executed, that a session is present in the context and therefore it can skip the error checking.
func indexHandler(w http.ResponseWriter, req *http.Request) {
session := req.Context().Value("session").(*sessions.Session)
err := views.ExecuteTemplate(w, "index.html", session.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
And to register the handler you would do:
app.HandleFunc("/", sessionMiddleware(indexHandler))
If this is still too much code repetition for your taste you can pass the session directly to your handlers however you'll have to change their signature.
type SessionHandler func(w http.ResponseWriter, r *http.Request, s *session.Session)
Then update your handlers.
func indexHandler(w http.ResponseWriter, req *http.Request, s *session.Session) {
err := views.ExecuteTemplate(w, "index.html", s.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
And you can define the middleware part on the SessionHandler type as a method.
func (h SessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
return
}
h(w, r, session)
}
And then to register the handler you would do:
app.Handle("/", SessionHandler(indexHandler))

golang session variables not working. what am i doing incorrectly?

I am using the gorilla/sessions package to implement sessions. the relevant code (or at least what I think is the only relevant part) is as follows:
// Function handler for executing HTML code
func lobbyHandler(w http.ResponseWriter, req *http.Request) {
if isLoggedIn := validateSession(w, req); isLoggedIn {
lobbyTempl.Execute(w, req.Host)
} else {
homeTempl.Execute(w, map[string]string{
"loginErrors": "Must log in first",
})
}
}
// Serves the files as needed, whenever they are requested
// used for all images, js, css, and other static files
func sourceHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
un, pw := r.FormValue("lUn"), r.FormValue("lPw")
if usr := findUser(un, pw); usr != nil {
if createSession(w, r) {
http.Redirect(w, req, "/lobby.html", http.StatusFound)
}
} else {
homeTempl.Execute(w, map[string]string{
"loginErrors": "User not found",
})
}
}
func createSession(w http.ResponseWriter, r *http.Request) bool {
session, _ := store.Get(r, sessionName)
session.Values["isAuthorized"] = true
if err := session.Save(r, w); err != nil {
fmt.Println("saving error: ", err.Error())
return false
}
return true
}
func validateSession(w http.ResponseWriter, r *http.Request) bool {
if session, err := store.Get(r, sessionName); err == nil {
if v, ok := session.Values["isAuthorized"]; ok && v == true {
fmt.Println("Authorized user identified!")
return true
} else {
fmt.Println("Unauthorized user detected!")
return false
}
}
return false
}
func main() {
//...
// serving files for the game
http.HandleFunc("/", homeHandler)
http.Handle("/ws", websocket.Handler(wsLobbyHandler))
http.HandleFunc("/lobby.html", lobbyHandler)
http.HandleFunc("/formlogin", loginHandler)
//...
//http.HandleFunc("/*.html", SourceHandler)
if err := http.ListenAndServeTLS(*addr, "cert.pem", "key.pem", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}`
in my html i have:
<form id="login_form" action="/formlogin" method="post">
When logging in, the request is handled within loginHandler
The user is identified correctly from the database and a session is created (via createSession()) and placed into the cookie store.
But after the redirect to lobby.html, back in loginHandler
http.Redirect(w, req, "/lobby.html", http.StatusFound)
the validation within lobbyHandler does not work. Does this have to do with the store.Save(...) altering the headers?
I'm very new to go, as well as web apps in general, so I would really appreciate feedback.
Thanks to the comments, i was able to stumble across a similar search that works for me.
session.Options = &sessions.Options{
Path: "/lobby.html",
}
I needed to make sure the cookies know where they are going to be redirected properly.

Resources