API Discovering its own URL Golang - go

I've created an API in Go that can search or directly access an Element via ElasticSearch, which then adds some data to the JSON payload, and returns that to the user. When searching I'm returning a list of Elements, and I'd like to be able to give a direct URL to the specific Element should the user wish to get more information.
However I can't ascertain how the application is supposed to figure out its own URL. In the event that it can't, what would be a reasonable alternative for the URL itself?
Using net/http package and gorilla/mux for net based things.

You can "reverse" a gorilla/mux url by looking up the route by name, and generating the url with Route.URL
You will want to name your route, so that you can easily look it up via Router.Get.
r := mux.NewRouter()
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
Name("article")
And once you have the route, you can build a url by passing the parameters to URL
url, err := r.Get("article").URL("category", "technology", "id", "42")
// "/articles/technology/42"
Note that if you want the host to be inserted automatically, it will need to be defined on your route:
r := mux.NewRouter()
r.Host("{subdomain}.domain.com").
HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
Name("article")
// url.String() will be "http://news.domain.com/articles/technology/42"
url, err := r.Get("article").URL("subdomain", "news",
"category", "technology",
"id", "42")

Related

show static image based on users in golang gin

I'm using the Gin framework. I have a database that contains some course info. Users can register in the courses and access the contents. The contents are image, video, and audio.
I store the relative location of these contents in my database like this:
Content\Courses\CourseOne\Unit_1\image\1.jpg
and change it to the actual location in gin:
route := gin.Default()
route.Static("/Content","./Media")
Everything works fine, but I am looking for a way to authenticate users before accessing the contents. In the above-mentioned way, all users can access any data by changing the desired pattern's address. But I want if the user is registered in the course, be able to access data, otherwise, get a 404 error.
how can I do that?
Edit
since it was asked to explain the implementation of authentication:
I used JWT for authentication. so each user has a HashID.
I have a table called UserCourses and the user info would be inserted after purchasing a course.
this is my course route:
route.GET("api/v1/courses", handler.GetCourses)
and my handler:
func GetCourses(context *gin.Context) {
hashID, status, err := repository.GetToken(context)
if err != nil {
context.IndentedJSON(status, err)
return
}
courses := make([]model.CourseAPI, 0)
userInfo := model.Users{HashID: hashID}
err = repository.DatabaseInstance.GetCourses(&courses, &userInfo)
if err != nil {
context.IndentedJSON(http.StatusServiceUnavailable, err)
return
}
context.IndentedJSON(http.StatusOK, gin.H{"courses": courses})
}
The JWT token is passed by the client in the header. so I get the token and validate it. The token contains the user HashID and I check for that HashID in the UserCourses table. besides the course info, there is a variable called isRegistered.if the HashID was registered for any course in UserCourses table,the isRegistered become true for that course otherwise false.
You can create group route and apply authentication middleware through it
r = gin.Default()
// public routes
r.GET("public", publicHandler)
// routes group
auth = r.Group("/")
// authentication middleware within group
auth.Use(AuthMiddleware())
// route before which auth middleware will be run
auth.Static("/Content","./Media")

Getting JWT data in Gorilla CustomLoggingHandler

I am using a custom logging handler in my Go web server like this:
func main() {
// ... Set up everything
router = mux.NewRouter()
router.Handle("/apilookup",
raven.Recoverer(
jwtMiddleware.Handler(
http.HandlerFunc(
doApiLookup)))).Methods("GET")
loggedRouter := handlers.CustomLoggingHandler(os.Stdout, router, writeLog)
http.ListenAndServe(listenAddr, loggedRouter)
}
In the writeLog function, I have made my own version of the Gorilla handlers.LoggingHandler, which logs a lot of additional information.
One thing I would like to do is log the user for authenticated requests. Users authenticate to this server using JWT (using the Authorization: Bearer ... header). I am using Auth0's go-jwt-middleware to parse the token and set its value in the Request's context.
I tried to log the user's email address (one of the claims in the JWT) like this, based on the middleware's documentation:
func writeLog(writer io.Writer, params handlers.LogFormatterParams) {
// ... SNIP
// If we can't identify the user
username := "-"
if userJwt := params.Request.Context().Value("user"); userJwt != nil {
claims := userJwt.(*jwt.Token).Claims.(*jwtClaims)
username = claims.Email
}
// ... SNIP
}
The problem is that username is always the initial value - and not the expected value from the JWT.
By adding log.Printf("%+v\n", params.Request.Context()) above the if, I see that the context doesn't actually contain the parsed JWT data here.
As far as I can tell, the reason this is not working is because the middleware creates a new Request with the updated context, so only middleware further down the chain can see it. Because the logging middleware is above the JWT middleware, it does not have that same context.
I know that I can re-parse the JWT in the logging handler (because I do have access to all the headers), but that seems like a lot of overhead for logging.
Is there a better way to do this that will allow me to have access to this data where I want it?

Is there any way to middleware router with group router?

I am beginner in beego framework, I have completed few R&D inside on it.
But I need few helps related routers.
I have created few route with middleware and group router but I need few suggestions from expert.
Let me share example which I did.
Router.go
func init() {
ns := beego.NewNamespace("/api/v1",
beego.NSNamespace("/front",
beego.NSBefore(AuthFilter),
beego.NSRouter("/user",&controllers.ObjectController{},"*:GetValueByAdmin"),
beego.NSRouter("/test",&controllers.ObjectController{},"*:GetValueByAdmin"),
beego.NSRouter("/test",&controllers.ObjectController{},"*:GetValueByAdmin"),
beego.NSRouter("/test",&controllers.ObjectController{},"*:GetValueByAdmin"),
beego.NSRouter("/product",&controllers.ObjectController{},"*:GetValueByAdmin"),
),
beego.NSNamespace("/a1",
beego.NSRouter("/test1",&controllers.ObjectController{},"*:GetValueByAdmin1"),
beego.NSRouter("/test2",&controllers.ObjectController{},"*:GetValueByAdmin1"),
beego.NSInclude(
&controllers.UserController{},
),
),
)
beego.AddNamespace(ns)
}
var AuthFilter = func(ctx *context.Context) {
// The Authorization header should come in this format: Bearer <jwt>
// The first thing we do is check that the JWT exists
header := strings.Split(ctx.Input.Header("Authorization"), " ")
if header[0] != "Bearer" {
ctx.Abort(401, "Not authorized")
}
}
I have created router using Namespace and It is working fine using this url (http://localhost:8080/api/v1/front/test). But I want to remove "front" keyword from URL.
I tried below options like:
I copied code inside "Front" namespace to put outside but My "NSBefore" Will apply all the method which is defined after that. I need 2 group. Before auth and after auth. In after auth, I want to add beego.NSBefore(AuthFilter).
I tried using policy but it will not work as I needed.
beego.Policy("/api/v1/front/*","*", AuthFilter)
beego.Policy("/api/v1/admin/*","*", AuthFilter)
If I will remove front from policy then it will apply all the URL.
Do we have any option to create group router without URL path and it will cover my concept?

Different middleware for different routes in negroni

I want to have different middleware for different path. My current implementation is from this link
UserRouter := mux.NewRouter().StrictSlash(true)
AdminRouter := mux.NewRouter().StrictSlash(true)
Router.HandleFunc("/apps/{app_name}/xyz", Handler).Methods("GET")
I created three different routers, so that I can assosiate them with different path and middleware
nUserPath := negroni.New(middleware.NewAuthMiddleWare())
nUserPath.UseHandler(UserRouter)
nAdminPath := negroni.New()
nAdminPath.UseHandler(AdminRouter)
I created two different negroni instances and passed them the respective routers. As I wanted all this to run part of the same application on the same port so I created a Wrapper Router and negroni instance and associated them with the existing like below
BaseRouter := mux.NewRouter().StrictSlash(true)
BaseRouter.Handle(UserBasePath,nUserPath) // UserBasePath is `/apps`
BaseRouter.Handle(HealthCheck,nUserPath) // HealthCheck is `/health`
BaseRouter.Handle(AdminBasePath,nAdminPath) // AdminBasePath is `/Admin`
n := negroni.New(middleware.NewLogger()) // attached other common middleware here
n.UseHandler(router.BaseRouter)
n.Run(":8080")
Issues faced in this approach:
When I run /health it runs properly but when I run /apps/{app_name}/something I get a 404: Not Found
Note : I went through other approaches mentioned in below link but they don't satisfy my need.
- Route-specific Middlewares with Negroni
So, the issue with the above implementation is that BaseRouter.Handle() method take a path and not a path_matcher/template so all the url's which has path_length more than one were not working.
I figured out two ways to achieve what I needed:
First approach
// Create a rootRouter
var rootRouter *mux.Router = mux.NewRouter()
// Create as many subRouter you want with some prefix
var appsBasePath string = "/apps"
var adminBasePath string = "/admin"
upRouter := rootRouter.PathPrefix(appsBasePath).Subrouter()
apRouter := rootRouter.PathPrefix(adminBasePath).Subrouter()
// Register all the paths and mention middleware specifically for all of them
// Here middleware is a method with signature as
// func middleware( http.Handler) http.HandlerFunc {}
upRouter.Path("/test").Methods("POST").Handler(middleware(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
fmt.Fprintf(w, "Welcome to the home page!")
})))
n := negroni.New(middleware.NewLogger()) // attached other common middleware here
n.UseHandler(rootRouter)
n.Run(":8080")
Second approach
This is extension/solution of the original issue in the question
// Replace BaseRouter.handle() as below
// as PathPrefix takes a template so it won't have issue that we were facing
BaseRouter.PathPrefix(UserBasePath).Handler(nUserPath)
Thing to remember here is that within negroni nUserPath RequestContext of the middleware attached will be different from that of the actual router's HandlerMethod
Note:
By path length I mean something like this -- /abc or /abc/ has path_length=1 and /abc/xyz has path_length=2

Go HTTP handler - handling more than two form actions

I have a webapp where I have code like:
func handler (w res, r req) {
if req.Method == POST {
// create html form 2 with a submit button2
return ;
}
// create html form 1 with a submit button1
return
}
func main() {
handle("/", handler)
}
Now, the root / is registered with the handler func. In the first request (GET), I create a form and send it to the user. If the user submits this form, I handle it under the "POST" method. Now, I create a different form in the post method handler, and now I want a way to do some operations based on what the user typed in this form2, when [s]he submits form2.
What is the standard way with go to handle the form2 form submission ? I have done some asp programming earlier and we use form action to submit to a different asp file. How can I do some actions based on parsing the form2 submission request ?
If I'm understanding you correctly, you want a way of routing the same URL to different handlers based on the request method rather than just the path? If that's the case...
For comparison, using Python + Django, the way you're doing this is pretty standard:
def my_django_view(request):
if request.method == "POST":
try_to_process_posted_data()
elif request.method == "GET":
show_a_form_to_user()
If you are trying to do fancier things like URL routing based on path and request method (GET, POST, DELETE, ...), then you might be interested in something like Gorilla.mux
It provides some friendly URL routing methods:
func main() {
router := mux.NewRouter()
router.HandleFunc("/", YourGETHandlerFunc).Methods("GET")
router.HandleFunc("/", YourPOSTHandlerFunc).Methods("POST")
http.Handle("/", router)
}
If you're looking for more resources for web development...
Mango: http://paulbellamy.com/2011/05/introducing-mango/
Web.go: http://www.getwebgo.com/tutorial
Twister: https://github.com/garyburd/twister/blob/master/examples/hello/main.go

Resources