Variables for goroutines - go

Im currently making a web app using Go. I want to know on my templates when the user is logged in or not and I am currently making it using this approach
response := &viewCharacter{true}
template.Renderer.ExecuteTemplate(w, "character_search.html", response)
As you see I am passing a viewCharacter struct that only contains a bool Logged then on a template I can do the following
{{ if .Logged }}
Is there any other approach to do this? instead of passing on each template a logged bool?
Maybe setting a variable for each goroutine of the http handler that saves if the user is logged or not?

There are only two ways that I know to communicate between the go code and the template.
The first one is the one you use. By passing a struct to the ExecuteTemplate function you can access all its field in the template.
The second one is to register other functions using:
func (t *Template) Funcs(funcMap FuncMap) *Template
See documentation for more information. The funcMap is a simple map[string]interface{}, you can register any function and call it in the template by using its name (the key of the map).

Related

gin gonic nested templates

Go base lib's template has method Execute which allows to reference another template in order to use template nesting, like follows:
template.Template.Execute("base", data)
Also see this SO question.
How can I achieve the same but inside gin tonic's http response handler? Typically it looks like this
func about(c *gin.Context) {
c.HTML(http.StatusOK, "about.tmpl", gin.H{})
}
Which leaves no way to reference another template like with execute.

Why should I make a copy of a context for goroutines inside handlers?

I recently started rewriting some of my Python services in Go to speed them up and came across this section of the gin documentation:
https://github.com/gin-gonic/gin#goroutines-inside-a-middleware
So I understand the instructions, but I'm trying to understand why? What is the significance of making a copy, and what problem is introduced if I do not make a copy of context for goroutines within handlers?
Gin-gonic is async itself which makes it great.
If you are using concurrency within your handlers it's very likely you will face situation when Context, which is struct goes
outdated since it's holding the data from each request (parameters, keys protected by mutex)
empty, which will cause a fallback to default Context
here's how it looks like:
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain
index int8
fullPath string
engine *Engine
params *Params
skippedNodes *[]skippedNode
// This mutex protects Keys map.
mu sync.RWMutex
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]any
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string
// queryCache caches the query result from c.Request.URL.Query().
queryCache url.Values
// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url.Values
// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests.
sameSite http.SameSite
}
In order to foresee such issues and race conditions - you must use Copy whenever your handlers are using go concurrency
here's a quote from gin-gonic repository:
// Copy returns a copy of the current context that can be safely used outside the request's scope.
// This has to be used when the context has to be passed to a goroutine.

In Go gin framework , how to get all url that I have registered in the router

I’m programming the role based access control with casbin. However ,the police need me to provide urls that the role has. The “v1” column in the table, see the picture.
I wondered can I get all urls that I registered in router. So that I don’t need to add by my hand.
You can use Routes which would return slice of RoutesInfo through which you can get all the register route path plus additional informaiton.
RoutesInfo will contain below struct from which required information can be retrieved.
type RouteInfo struct {
Method string
Path string
Handler string
HandlerFunc HandlerFunc
}
r := gin.Default()
r.Routes()

What's the easiest way to define a custom HTML-stringification?

Is there a way for me to define that when I just output a value in Go html templates the way the formatting is done instead of it printing a string representation without having to call a function to explicitcly convert it?
For example, let's say I have a type Person and I have a template with just {{.}} I want it to automatically create a link to that person's page but if I use the same template and passed a value of a different type some other HTML will be generated.
What I don't want to do is having to write something like {{.HTML}} or {{. | html}}. I'm already aware that these are possible but my question is specifically about how to avoid those.
I've played around with the thought of Person.String() having return the HTML code somehow without it being escaped but besides not getting that to work it also seems like an ugly solution.
Another solution I've thought about is to just pass everything as HTML into the template but then I couldn't access the attributes anymore (like {{.name}} to output just the name) and I'd also have to convert everything into HTML just in case it's used in the template.
Create a method that returns an template.HTML type. i.e.:
func (p *Person) HTML() template.HTML {
return fmt.Sprintf(`%s`, p.id, template.HTMLEscapeString(p.name))
}
Then in your template:
{{ .HTML }}

How is gorilla/context different from gorilla/sessions?

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?
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:
Request comes in
CSRF middleware checks the session for an existing CSRF token. Does not exist, so it sets one.
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)
Request is done.
New request on form submission
The token still persists in the session, so we can compare it to the submitted token from the form.
If it checks out, we proceed to process the form
If not, we can save an error in the session (a flash message; i.e. one that is erased after reading) and re-direct.
This re-direction is a new request, and therefore we can't pass the error message via the request context here.
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.

Resources