passing named parameters to index template while using httprouter golang package - go

I just learnt how to use the httprouter go package and read many documents about it but
failed to use the :name style of passing paramenters toe templates when it comes to the index
page template.
ex.
My router code:
func getRouter() *httprouter.Router {
// Load and parse templates (from binary or disk)
templateBox = rice.MustFindBox("templates")
templateBox.Walk("", newTemplate)
// mux handler
router := httprouter.New()
// Example route that encounters an error
router.GET("/broken/handler", broken)
// Index route
router.GET("/:email", index)
router.GET("/read/all", readingHandler)
router.POST("/submit/newcon", Handler1)
router.POST("/read/newcon", Handler2)
// Serve static assets via the "static" directory
fs := rice.MustFindBox("static").HTTPBox()
router.ServeFiles("/static/*filepath", fs)
return router
}
Then i get this error:
panic: wildcard segment ':email' conflicts with existing children in path '/:email'

so it should work with /user/:email instead of just /:email.

Related

How to serve static files from all routes except one in Gin? [duplicate]

This question already has answers here:
Gin router: path segment conflicts with existing wildcard
(2 answers)
Closed 9 months ago.
As the title says, consider a Gin router where I want to serve static files from all routes except one. Let's say this one route is /api. A first attempt might look like this:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.StaticFS("/", gin.Dir("static", false))
r.GET("/api/v1/foo", func(c *gin.Context) { c.JSON(200, gin.H{"foo": true}) })
r.Run(":9955")
}
The RouterGroup.StaticFS (and Static too) under the hood joins the relative path with a wildcard path param: path.Join(relativePath, "/*filepath"). When relativePath is the root path /, it will panic with:
panic: '/api/v1/foo' in new path '/api/v1/foo' conflicts with existing wildcard '/*filepath' in existing prefix '/*filepath'
This is due to Gin's http router implementation: routing matches on the path prefix, so a wildcard on root will conflict with every other route. More details about this behavior can be found here — which is where this question was raised.
Another possible solution is to prefix the static file route, so that it doesn't conflict with /api:
r.StaticFS("/static", gin.Dir("static", false))
but this doesn't allow me to serve assets from root too. How can I have a wildcard, or equivalent, on root and still match on one specific path?
There is a package called static for this.
https://github.com/gin-contrib/static#canonical-example
just modify the example as follows
package main
import (
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// server a directory called static
r.Use(static.Serve("/", static.LocalFile("static", false)))
// And any route as usual
r.GET("/api/ping", func(c *gin.Context) {
c.String(200, "ping")
})
r.Run(":9955")
}

List custom resources from caching client with custom fieldSelector

I'm using the Operator SDK to build a custom Kubernetes operator. I have created a custom resource definition and a controller using the respective Operator SDK commands:
operator-sdk add api --api-version example.com/v1alpha1 --kind=Example
operator-sdk add controller --api-version example.com/v1alpha1 --kind=Example
Within the main reconciliation loop (for the example above, the auto-generated ReconcileExample.Reconcile method) I have some custom business logic that requires me to query the Kubernetes API for other objects of the same kind that have a certain field value. It's occurred to me that I might be able to use the default API client (that is provided by the controller) with a custom field selector:
func (r *ReconcileExample) Reconcile(request reconcile.Request) (reconcile.Result, error) {
ctx := context.TODO()
listOptions := client.ListOptions{
FieldSelector: fields.SelectorFromSet(fields.Set{"spec.someField": "someValue"}),
Namespace: request.Namespace,
}
otherExamples := v1alpha1.ExampleList{}
if err := r.client.List(ctx, &listOptions, &otherExamples); err != nil {
return reconcile.Result{}, err
}
// do stuff...
return reconcile.Result{}, nil
}
When I run the operator and create a new Example resource, the operator fails with the following error message:
{"level":"info","ts":1563388786.825384,"logger":"controller_example","msg":"Reconciling Example","Request.Namespace":"default","Request.Name":"example-test"}
{"level":"error","ts":1563388786.8255732,"logger":"kubebuilder.controller","msg":"Reconciler error","controller":"example-controller","request":"default/example-test","error":"Index with name field:spec.someField does not exist","stacktrace":"..."}
The most important part being
Index with name field:spec.someField does not exist
I've already searched the Operator SDK's documentation on the default API client and learned a bit about the inner workings of the client, but no detailed explanation on this error or how to fix it.
What does this error message mean, and how can I create this missing index to efficiently list objects by this field value?
The default API client that is provided by the controller is a split client -- it serves Get and List requests from a locally-held cache and forwards other methods like Create and Update directly to the Kubernetes API server. This is also explained in the respective documentation:
The SDK will generate code to create a Manager, which holds a Cache and a Client to be used in CRUD operations and communicate with the API server. By default a Controller's Reconciler will be populated with the Manager's Client which is a split-client. [...] A split client reads (Get and List) from the Cache and writes (Create, Update, Delete) to the API server. Reading from the Cache significantly reduces request load on the API server; as long as the Cache is updated by the API server, read operations are eventually consistent.
To query values from the cache using a custom field selector, the cache needs to have a search index for this field. This indexer can be defined right after the cache has been set up.
To register a custom indexer, add the following code into the bootstrapping logic of the operator (in the auto-generated code, this is done directly in main). This needs to be done after the controller manager has been instantiated (manager.New) and also after the custom API types have been added to the runtime.Scheme:
package main
import (
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"example.com/example-operator/pkg/apis/example/v1alpha1"
// ...
)
function main() {
// ...
cache := mgr.GetCache()
indexFunc := func(obj k8sruntime.Object) []string {
return []string{obj.(*v1alpha1.Example).Spec.SomeField}
}
if err := cache.IndexField(&v1alpha1.Example{}, "spec.someField", indexFunc); err != nil {
panic(err)
}
// ...
}
When a respective indexer function is defined, field selectors on spec.someField will work from the local cache as expected.

Problems running binary created by Go build

I have a simple Go application, it has a few template files where I render some text. After I build my binary with Go build I try to run the file and I get the error:
panic: html/template: pattern matches no files: public/*.html
I am using the Echo framework and have followed their steps on adding a render for templates.
Here is the code in my main.go file
// TemplateRenderer is a custom html/template renderer for Echo framework
type TemplateRenderer struct {
templates *template.Template
}
// Render renders a template document
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func main() {
// Create a new instance of Echo
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
renderer := &TemplateRenderer{
templates: template.Must(template.ParseGlob("public/*.html")),
}
e.Renderer = renderer
e.GET("/", func(context echo.Context) error {
return context.Render(http.StatusOK, "index.html", api.FetchCoinList())
})
}
Is there something I have to do to package my templates up in the binary? It works perfectly when I run go run main.go
Is there something I have to do to package my templates up in the binary?
Yes, make them avialable at the same (relative) folder where they are present when you run them with go run main.go.
For example if there is a public folder containing the templates next to your main.go, then make sure you "copy" the public folder next to your executable binary.
Read this question+answer for more options: how to reference a relative file from code and tests
Usually you should provide ways to define where to get static assets and files from. The app may have a default place to look for them, but it should be easy to change this setting (e.g. via command line flags, via environment variables or via config files).
Another option is to include static files in the executable binary. Check out this question how to do that: What's the best way to bundle static resources in a Go program?

Golang best way of calling/invoking sub project

Im moving my project from PHP to Golang and I looking efficient way of calling/invoking/handling control to sub project main.go from src main.go, I want to pass control from
http://localhost/ => http://localhost/sub-project1/
http://localhost/ => http://localhost/sub-project2/
http://localhost/ => http://localhost/sub-projectn/
I'm new to Golang I don't know how to do it in best way, and my project structure is
src/
main.go
sub-project1/
main.go
sub-project2/
main.go
sub-projectn/
main.go
gitHub.com/
......
golang.org/
......
I'm using httprouter for routing, in main.go which is located under src contain following
package main
import ....
// homePageHandler
// contactPageHandler
// aboutPageHandler
// loginPageHandler
// signupPageHandler
func main() {
router := httprouter.New()
router.GET("/", homePageHandler)
router.GET("/contact", contactPageHandler)
router.GET("/about", aboutPageHandler)
router.GET("/login", loginPageHandler)
router.GET("/signup", signupPageHandler)
// here I want to pass control to my sub project main.go
// and I don't want to write any /sub-project routing urls here,
// because each /sub-project's contain many urls
router.GET("/sub-project1", ??????)
router.GET("/sub-project2", ??????)
router.GET("/sub-project3", ??????)
router.GET("/sub-projectn", ??????)
}
and all files must be passes from src main.go because whole project has only one main() and inside any /sub-projectx main.go I want to do this
package main
import ....
// subprojectPageHandler
// feature1PageHandler
// feature2PageHandler
// feature3PageHandler
// ........
// featurenPageHandler
func main() {
router := httprouter.New()
router.GET("/sub-projectx", subprojectPageHandler)
router.GET("/sub-projectx/feature1", feature1PageHandler)
router.GET("/sub-projectx/feature2", feature2PageHandler)
router.GET("/sub-projectx/feature3", feature3PageHandler)
..........
router.GET("/sub-projectx/featureN", featureNPageHandler)
// here I want to pass control to my sub project main.go
// and I don't want to write any /sub-project routing urls here,
// because each /sub-project's contain many urls
router.GET("/sub-project1", ??????)
router.GET("/sub-project2", ??????)
router.GET("/sub-project3", ??????)
router.GET("/sub-projectn", ??????)
}
To be
Golang source code is not running through interpreter, but is built into a binary, which gives less flexibility in case of dynamic projects. That said, I'd keep my projects isolated one from another, and would let Nginx (for example) take care of multiple project grouping. Of course, that would require some refactoring like creating shared packages, etc.
Or not to be
If, for some reason, you still think running multiple projects via single binary is ok, it's your choice. In this case you might have a look into route grouping that are available in some frameworks. Here's what Go Gin provides:
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}

How to make templates work with gin framework?

I am newbie to golang.
To learn it I have started with a simple web app using gin framework.
I have followed the gin doc & configured template file but not able to make it work. I am getting an error -
panic: html/template: pattern matches no files: `templates/*`
goroutine 1 [running]:
html/template.Must
/usr/local/Cellar/go/1.5.2/libexec/src/html/template/template.go:330
github.com/gin-gonic/gin.(*Engine).LoadHTMLGlob
/Users/ameypatil/deployment/go/src/github.com/gin-gonic/gin/gin.go:126
main.main()
/Users/ameypatil/deployment/go/src/github.com/ameykpatil/gospike/main.go:17
Below is my code -
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
//os.Setenv("GIN_MODE", "release")
//gin.SetMode(gin.ReleaseMode)
// Creates a gin router with default middleware:
// logger and recovery (crash-free) middleware
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/index.tmpl")
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "GoSpike",
})
})
// By default it serves on :8080 unless a
// PORT environment variable was defined.
router.Run(":4848")
}
My directory structure is
- gospike
--- templates
------index.tmpl
--- main.go
go install command does not give any error
but on actually running, it gives the above error. I searched & there were similar issues logged on gin's github repo but they are closed now.
I have tried various things but I guess I am missing something obvious. What am I missing?
I'm guessing the issue is that you're using a relative filepath to access your templates.
If I compile and run your code from the gospike directory, it works fine. But if I run gospike from any other directory, I get the same error you were seeing.
So either you need to always run gospike in the parent directory of templates, or you need to use the absolute path. You could either hard code it:
router.LoadHTMLGlob("/go/src/github.com/ameykpatil/gospike/templates/*")
or you could do something like
router.LoadHTMLGlob(filepath.Join(os.Getenv("GOPATH"),
"src/github.com/ameykpatil/gospike/templates/*"))
but that will fail if you have multiple paths set in your GOPATH. A better long-term solution might be setting a special environment variable like TMPL_DIR, and then just using that:
router.LoadHTMLGlob(filepath.Join(os.Getenv("TMPL_DIR"), "*"))
use relative path glob will working, you can try to code
router.LoadHTMLGlob("./templates/*")
notice the . dot sign which meaning current directory, gin.Engine will load template
base on templates subdirectory of current directory .
Here is how I do it. This walks through the directory and collects the files marked with my template suffix which is .html & then I just include all of those. I haven't seen this answer anywhere so I thought Id post it.
// START UP THE ROUTER
router := gin.Default()
var files []string
filepath.Walk("./views", func(path string, info os.FileInfo, err error) error {
if strings.HasSuffix(path, ".html") {
files = append(files, path)
}
return nil
})
router.LoadHTMLFiles(files...)
// SERVE STATICS
router.Use(static.Serve("/css", static.LocalFile("./css", true)))
router.Use(static.Serve("/js", static.LocalFile("./js", true)))
router.Use(static.Serve("/images", static.LocalFile("./images", true)))
routers.LoadBaseRoutes(router)
routers.LoadBlog(router)
router.Run(":8080")
There is a multitemplate HTML render to support multi tempaltes.
You can use AddFromFiles to add multi files:
r.AddFromFiles("index", "templates/base.html", "templates/index.html")
r.AddFromFiles("article", "templates/base.html", "templates/index.html", "templates/article.html")
Or load multi files under a directory:
package main
import (
"path/filepath"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
r.HTMLRender = loadTemplates()
// ...
}
func loadTemplates() multitemplate.Render {
files, err := filepath.Glob("template/*.tmpl")
if err != nil {
panic("failed to load html templates: " + err.Error())
}
r := multitemplate.New()
// Generate our templates map from our template/ directories
for _, file := range files {
r.AddFromFiles(filepath.Base(file), file)
}
// add other html templates directly
r.Add("test.tmpl", someTemplate)
return r
}
You could see more APIs in this repo you want, hope this post help :)

Resources