How is gorilla/context different from gorilla/sessions?

3.3k views Asked by At

I get sessions, coming from PHP I used to

<?php
session_start();
$_SESSION["key"] = "val";
echo $_SESSION["key"];
?>

Set one or more keys and their values serverside and be able to retrieve or overwrite it until the session expires.

The same with gorilla/sessions

var(
    sessionStore  *sessions.CookieStore
    sessionSecret []byte = make([]byte, 64)
    session       *sessions.Session
)

func init(){
    sessionSecret = []byte("12345678901234567890123456789012")
    sessionStore = sessions.NewCookieStore(sessionSecret)
    session = sessions.NewSession(sessionStore, "session_name")
}

func SetSessionHandler(w http.ResponseWriter, r *http.Request) {
    session, _ = sessionStore.Get(r, "session_name")
    session.Values["key"] = "val"
    session.Save(r, w)
}

func GetSessionHandler(w http.ResponseWriter, r *http.Request) {
    session, _ = sessionStore.Get(r, "session_name")
    fmt.FPrintln(session.Values["key"])
}

Now I don't get what the point of gorilla/context is. I know what a context is but... I don't know how it fits in the big picture. It says that it's bound to the current request. Another question here on stackoverflow said that "simply using gorilla/context should suffice" in the context of Writing Per-Handler Middleware.

But if it's request bound... err.. syntax error, does not compute. If a duck floats on water then witches are made from wood. And because ducks also float on water if her weight is the same as that of a duck she must be a witch. Or something like that ;)

And how could this be useful as a middleware "manager" when it's request-bound, I can't set it globally. Could you perhaps show an example of how a gorilla/sessions could be used with gorilla/context?

2

There are 2 answers

3
elithrar On BEST ANSWER

As the person who asked that other question:

  • gorilla/context allows you to store data in the request. If you have some middleware that does some pre-processing on a request before deciding to continue (i.e. anti-CSRF), you might want to store a token in the request so your handler can pass it to the template. The gorilla/context documentation explains it well:

... a router can set variables extracted from the URL and later application handlers can access those values, or it can be used to store sessions values to be saved at the end of a request. There are several others common uses.

  • You may also want to store data in the session: error messages from form submissions, a user ID, or the 'canonical' version of the CSRF token for that visitor will likely be stored here. If you try to store an error message in the request context, and then re-direct the user, you'll lose it (that's a new request).

So why would you use context over sessions? It's lighter, and allows you to de-couple parts of your application (often, HTTP middleware!) from each other.

Example:

  1. Request comes in
  2. CSRF middleware checks the session for an existing CSRF token. Does not exist, so it sets one.
  3. It also passes this new token (via the request context!) to the handler that renders your form, so it can render it in the template (otherwise you would have to pull the token from the session again, which is wasted effort)
  4. Request is done.
  5. New request on form submission
  6. The token still persists in the session, so we can compare it to the submitted token from the form.
  7. If it checks out, we proceed to process the form
  8. If not, we can save an error in the session (a flash message; i.e. one that is erased after reading) and re-direct.
  9. This re-direction is a new request, and therefore we can't pass the error message via the request context here.
0
AudioBubble On

An example.

I'm writing this multi-community-forum software. Now I have a gorilla/mux router that serves different content for the main domain and another router that serves different content filtered by subdomain.domain.tld.

Context is very useful here, else you would repeat yourself over and over again. So if I know that for the subdomain router every request would do string operations to find out the subdomain name and check if it exists in the database I could just do this here (in context) for every request and then store the subdomain name in a context variable. And likewise if a forum's category slug or forum slug or a thread slug is set pass it to the handler, keep the processing that needs to be done in "context" and have less code in your handlers.

So the advantage of context is essentially to keep code DRY.

Like elihrar wrote, his example of a CSRF token. If you know that you need to check for the CSRF token on each request - don't duplicate this check in every handler that needs to do this, instead write a context wrapper ( / http.Handler) and do this for every request.