Determining if delete actually removed an existing key in a map - go

I have a map called nearby
func Delete(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
delete(nearby, params["id"])
}
I want to find out if the delete() call actually found a key to delete, I tried reading the return value:
func Delete(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
result := delete(nearby, params["id"])
}
but the compiler didn't like that - how can I find out if a key/val was deleted?

Probe the map before deleting the value:
func Delete(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
_, deleted := nearby[params["id"]]
delete(nearby, params["id"])
fmt.Println(deleted)
}
This snippet and the code in the question have a data race because HTTP handlers can be called concurrently. Add a mutex to protect the map.
var (
nearby = make(map[string]string)
mu sync.Mutex
)
func Delete(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
mu.Lock()
_, deleted := nearby[params["id"]]
delete(nearby, params["id"])
mu.Unlock()
fmt.Println(deleted)
}

The Go builtin delete() doesn't return anything, so you can't tell whether it deleted anything.
But you can check if the map contains the key and delete() it if it is present.
if _, ok := nearby[params["id"]]; ok {
delete(nearby, params["id"])
} else {
// whatever
}

Related

Get all data from a table in golang gorm

I'm new in golang and programming at all, so I've a problem with this function that supposed gets it all values from a table, but just shows me one. Thanks you all for your knowledge :)
func GetAll(w http.ResponseWriter, r *http.Request) {
results := map[string]interface{}{}
c, _ := connection.GetDB()
c.Table("products").Order("id_producto asc").Find(&results)
fmt.Print(results)
jsonString, _ := json.Marshal(results)
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(jsonString))
}
Try to create a DTO struct and create a list that contains item of type that struct. Then pass that list to the query. Like this:
type Product struct {
ProductID string
}
func GetAll(w http.ResponseWriter, r *http.Request) {
products := []*Product{}
c, _ := connection.GetDB()
c.Order("id_producto asc").Find(&products).
jsonString, _ := json.Marshal(products)
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(jsonString))
}
You can code like this:
func GetAll(w http.ResponseWriter, r *http.Request) {
// New a slice to get the results,
// map[string]interface{}{} will only scan one item of results
results := []map[string]interface{}{}
c, _ := connection.GetDB()
c.Table("products").Order("id_producto asc").Find(&results)
fmt.Print(results)
jsonString, _ := json.Marshal(results)
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(jsonString))
}

How to propagate value from child middleware to parent?

I am trying to customize request pipeline through middleware pattern, the code as follow:
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello, middleware!")
}
func middleware1(next http.HandlerFunc) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("[START] middleware1")
ctx := r.Context()
ctx = context.WithValue(ctx, middleware1Key, middleware1Value)
r = r.WithContext(ctx)
next(w, r)
fmt.Println("[END] middleware1")
ctx = r.Context()
if val, ok := ctx.Value(middleware2Key).(string); ok {
fmt.Printf("Value from middleware2 %s \n", val)
}
}
}
func middleware2(next http.HandlerFunc) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("[START] middleware2")
ctx := r.Context()
if val, ok := ctx.Value(middleware1Key).(string); ok {
fmt.Printf("Value from middleware1 %s \n", val)
}
ctx = context.WithValue(ctx, middleware2Key, middleware2Value)
r = r.WithContext(ctx)
next(w, r)
fmt.Println("[END] middleware2")
}
}
func main() {
mux := http.NewServeMux()
middlewares := newMws(middleware1, middleware2)
mux.HandleFunc("/hello", middlewares.then(helloHandler))
if err := http.ListenAndServe(":8080", mux); err != nil {
panic(err)
}
}
and the output is :
[START] middleware1
[START] middleware2
Value from middleware1 middleware1Value
Hello, middleware!
[END] middleware2
[END] middleware1
According to the output, the value could pass from parent to the child , while, if the child add something to the context, it is invisible to the parent
How can I propagate value from child middleware to parent?
What you're doing is creating a new pointer to the modified http.Request via WithContext method. So if you're passing it to next middleware in the chain everything works as expected since you're passing this new pointer as an argument.
If you want to modify the request and make it visible for those who hold the pointer to it, you need to dereference the pointer and set the modified value.
So in your 'child' middleware instead of:
r = r.WithContext(ctx)
Just do the following:
*r = *r.WithContext(ctx)
Good exercise to understand pointers in Go but you SHOULD NOT do similar operations in your production code. The docs are clear about it. See https://pkg.go.dev/net/http#Handler.
Another possible solution (without messing with the request itself) is to pass a map inside a context and read/write from/to map instead. So in your first middleware:
ctx := r.Context()
m := make(map[string]string)
m[middleware1Key] = middleware1Value
ctx = context.WithValue(ctx, dummyStoreKey, m)
r = r.WithContext(ctx)
...
if val, ok := m[middleware2Key]; ok {
fmt.Printf("Value from middleware2 %s \n", val)
}
And in the second one:
ctx := r.Context()
if store, ok := ctx.Value(dummyStoreKey).(map[string]string); ok {
if val, ok := store[middleware1Key]; ok {
fmt.Printf("Value from middleware1 %s \n", val)
}
store[middleware2Key] = middleware2Value
}
You could add a AddStoreMiddleware as the first one in the pipeline and then use it in each successor if needed. Remember, maps in Go are not concurrently safe so in some subtle cases you should serialize access.

Pass uninitialized struct to a function

Let say I have a function that handles request body in general
func GetReqBody(r *http.Request) (interface {}, error){
var data interface{}
decorder := json.NewDecoder(r.Body)
decorder.DisallowUnknownFields()
err := decorder.Decode(&data)
return data, err
}
Then in the controller, I will have to do type assertion
func post(w http.ResponseWriter, r *http.Request) {
data, err := utils.GetReqBody(r)
//req.User is a struct
newUser, ok := data.(req.User)
// ...
}
Is it possible to encapsulate the type assertion login inside the GetReqBody function? To do that I will need to pass the struct into the function, yet as it is not a value I am unable to do so.
"Is it possible to encapsulate the type assertion login inside the GetReqBody function?" -- No, it's not possible, not in any useful way.
However you could simplify your code thus:
func GetReqBody(r *http.Request, data interface{}) error {
decorder := json.NewDecoder(r.Body)
decorder.DisallowUnknownFields()
return decorder.Decode(data)
}
func post(w http.ResponseWriter, r *http.Request) {
var newUser req.User
if err := utils.GetReqBody(r, &newUser); err != nil {
// handle err
}
// ...
}

Lookup a string with a function

I'm designing a router API and I'd like to be able to lookup a path by its function. Something like:
createUser := func(w http.ResponseWriter, r *http.Request) {
// create a user
}
createPost := func(w http.ResponseWriter, r *http.Request) {
// create a post
}
router.Post("/users", createUser)
router.Post("/posts", createPost)
fmt.Println(router.Lookup(createPost))
Here's a playground link: https://play.golang.org/p/ec6U0jJUbfx
This is surprisingly hard to do because you can't test for equality on a function or stick it as a key in a map. Is this even possible?
Are there any other workarounds I'm not thinking of? A reflect solution would be just fine.
You can create a server struct with a ServerHTTP method that handles all the request. When there is a request you can look up for an specific method by the path and function
Here is an example:
type Server struct {
routes []route
}
func (s *Server) handlerServer(db mydb.IDB, ctx context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := ServerContext{w, r, db, ctx}
for i := 0; i < len(s.routes); i++ {
currentRoute := s.routes[i]
if isValidMethod(currentRoute, r) {
err := currentRoute.h(&ctx)
if err != nil {
log.Fatal(err)
}
}
}
}
}
I hope this is helpful

Best practice with sessions (gorilla/sessions)

Before starting using sessions in golang I need answers to some questions
session example
import "github.com/gorilla/sessions"
var store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))
func Handler(w http.ResponseWriter, r *http.Request){
session, _ := store.Get(r, "session-name")
session.Values["foo"] = "bar"
session.Values[42] = 43
session.Save(r, w)
fmt.Fprint(w, "Hello world :)")
}
func main(){
store.Options = &sessions.Options{
Domain: "localhost",
Path: "/",
MaxAge: 60 * 15,
Secure: false,
HttpOnly: true,
}
}
Q1:
Is it possible to add multiple sessions on the same domain with different names?
session1, _ := store.Get(r, "session-name-1")
session2, _ := store.Get(r, "session-name-2")
When do you need multiple sessions on the same domain?
Q2:
What is the best practice to get the variables from the session?
my_session_var = session.Values["foo"]
Q3:
How to check if the session is saved correctly? If you access the same map to both set and get variables?
update
package main
import (
"github.com/gorilla/sessions"
)
var (
store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))
)
type handler func(w http.ResponseWriter, r *http.Request, s *sessions.Session)
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request){
session, _ := store.Get(r, "session-name")
h(w, r, session)
}
func Handler_404(w http.ResponseWriter, r *http.Request, s *sessions.Session){
fmt.Fprint(w, "Oops, something went wrong!")
}
error
# command-line-arguments
.\mux.go:101: cannot convert Handler_404 (type func(http.ResponseWriter, *http.Request, *sessions.Session)) to type http.HandlerFunc
The article "BASIC EXTENSION OF GO’S HTTP HANDLERS" (Simon Whitehead) shows an example of where and when to define session.
Instead of doing it in the Handler itself, and having to duplicate a lot of code when you define other Handlers.
With a named type, you can define the Handler you need:
type handler func(w http.ResponseWriter, r *http.Request, db *mgo.Database)
(in your case, it would be a gorilla sessions instead of a mgo session or database)
The init() function can take care of the session creation (here mgo session, but the idea is the same for other framework sessions)
func init() {
session, err = mgo.Dial("localhost")
if err != nil {
log.Println(err)
}
}
And you can make sure this function type ('handler') does respect the ServeHTTP() function, taking care of:
the session management (clone/close)
calling your actual handler (which can have more parameters than just w and r)
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s := session.Clone()
defer s.Close()
h(w, r, s.DB("example"))
}
Then you can define your actual Handler (again, with more than w and r):
func myHandler(w http.ResponseWriter, r *http.Request, db *mgo.Database) {
var users []user
db.C("users").Find(nil).All(&users)
for _, user := range users {
fmt.Fprintf(w, "%s is %d years old", user.Name, user.Age)
}
}
And you can use that handler in your server:
func main() {
mux := http.NewServeMux()
mux.Handle("/", handler(myHandler))
http.ListenAndServe(":8080", mux)
}
The idea is to limit the "plumbing" in main() to a minimum, while having an Handler with more parameters (including your session).
That allows you to use different Handlers with very little plumbing, keeping main() only for the declaration of the different path (and not for the initialization of session and handlers)
Update 2019: in another related context, see also "How to handle sessions".

Resources