I have a URL that looks something like this: http://localhost/templates/verify?key=ijio
My router looks like this:
import (
"github.com/gorilla/mux"
"github.com/justinas/alice"
)
ctx := &model.AppContext{db, cfg} // passes in database and config
verifyUser := controller.Verify(ctx)
mx.Handle("/verify", commonHandlers.ThenFunc(verifyUser)).Methods("GET").Name("verify")
I want to get the key parameter out of the URL, so I use the following code:
func Verify(c *model.AppContext) http.HandlerFunc {
fn := func(w http.ResponseWriter, r *http.Request) {
key := r.URL.Query().Get("key") // gets the hash value that was placed in the URL
log.Println(key) // empty key
log.Println(r.URL.Query()) // returns map[]
// code that does something with key and sends back JSON response
}
}
I used AngularJS to get the JSON data:
app.controller("verifyControl", ['$scope', '$http', function($scope, $http) {
$scope.message = "";
$http({
method: 'GET',
url: "/verify"
}).success(function(data) {
$scope.message = data.msg; // JSON response
});
}]);
However, I end up with an empty key variable when I try to print it out. I recently used nginx to take out my .html extension if that's a possible cause of this problem. How do I fix this?
The solution to my question involves inspecting the request URL link by
log.Print(r.URL) // This returns "/verify"
However, that's not exactly what you want. Instead, you want the full URL. You can do the following to get the full URL and extract the parameter from it:
urlStr := r.Referer() // gets the full URL as a string
urlFull, err := url.Parse(urlStr) // returns a *URL object
if err != nil {
log.Fatal(err)
return
}
key := urlFull.Query().Get("key") // now we get the key parameter from the URL
log.Println("Key: " + key) // now you'll get a non empty string
Related
I'm trying to write a "Binder" middleware that will validate any request query using a struct type with gin bindings/validators
So for example, let's say I have an endpoint group called /api/subject which requires the query string to have a subject code and an ID that will be validated using the following struct (called entity.Subject):
type Subject struct {
Code string `binding:"required,alphanum"`
ID string `binding:"required,alphanum,len=4"`
}
That's just one example, but I'd like to be able to pass any struct type to this middleware, because I'd like to access the query data on future handlers without worrying about query validation.
So I tried something like this:
func Binder(t reflect.Type) gin.HandlerFunc {
return func(c *gin.Context) {
obj := reflect.New(t).Elem().Interface()
if err := c.BindQuery(&obj); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
c.Set(t.Name(), obj)
}
}
And added this middleware like so:
apiGroup := router.Group("/api")
{
// other subgroups/endpoints
// ...
subjectGroup := apiGroup.Group("/subject", middleware.Binder(reflect.TypeOf(entity.Subject{})))
}
And later on, in another handler function, let's say GetSubject, I want to access the subject data passed by doing c.MustGet("Subject").(entity.Subject)
But this isn't working =(, when I print obj, it's just an empty interface, how would I do this?
I managed to do something similar!
I created the following middleware
var allowedTypes = []binding.Binding{
binding.Query,
binding.Form,
binding.FormPost,
binding.FormMultipart,
}
func Bind(name string, data interface{}, bindingType binding.Binding) gin.HandlerFunc {
return func(ctx *gin.Context) {
ok := false
for _, b := range allowedTypes {
if b == bindingType {
ok = true
}
}
if !ok {
ctx.AbortWithError(
http.StatusInternalServerError,
fmt.Errorf("Bind function only allows %v\n", allowedTypes),
)
}
_ = ctx.MustBindWith(data, bindingType)
ctx.Set(name, data)
}
}
Remember to pass a pointer to your desired type in the call, like so:
router.GET("/something", Bind("Object", &myObject, binding.Query))
I restricted only to a few binding types because they allow ShouldBind to be called multiple times, whereas JSON, XML and others consume the Request body.
This way you can pass multiple Bind middlewares and if the validation fails it automatically aborts with http.StatusBadRequest
I'm trying to make a simple API call to the pokemon API through reaching a POST request that I'm serving with Echo.
I'm sending a POST request to "localhost:8000/pokemon" with the body { "pokemon": "pikachu" } where the BODY is reattached to the request through ioutil changing the request to be made with the body: "localhost:8000/pokemon/pikachu".
The POST request works by responding with some JSON, but the call being made is only to "localhost:8000/pokemon", and it seems the body isn't added to the URL.
I think there is something wrong with the binding here u := new(pokemon)
Anyone have any ideas?
func main() {
e := echo.New() // Middleware
e.Use(middleware.Logger()) // Logger
e.Use(middleware.Recover())
//CORS
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}))
// Root route => handler
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
})
e.POST("/pokemon", controllers.GrabPrice) // Price endpoint
// Server
e.Logger.Fatal(e.Start(":8000"))
}
type pokemon struct { pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}
// GrabPrice - handler method for binding JSON body and scraping for stock price
func GrabPrice(c echo.Context) (err error) {
// Read the Body content
var bodyBytes []byte
if c.Request().Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request().Body)
}
// Restore the io.ReadCloser to its original state
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
u := new(pokemon)
er := c.Bind(u) // bind the structure with the context body
// on no panic!
if er != nil {
panic(er)
}
// company ticker
ticker := u.pokemon
print("Here", string(u.pokemon))
// yahoo finance base URL
baseURL := "https://pokeapi.co/api/v2/pokemon"
print(baseURL + ticker)
// price XPath
//pricePath := "//*[#name=\"static\"]"
// load HTML document by binding base url and passed in ticker
doc, err := htmlquery.LoadURL(baseURL + ticker)
// uh oh :( freak out!!
if err != nil {
panic(err)
}
// HTML Node
// from the Node get inner text
price := string(htmlquery.InnerText(doc))
return c.JSON(http.StatusOK, price)
}
Adding to what already answered by #mkopriva and #A.Lorefice
Yes you need to ensure that the variable are exported, for the binding to work properly.
Since underlay process of binding actually using reflection mechanism on the struct. See this documentation, scroll into Structs section to see what it is.
type pokemon struct {
Pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}
I have the following code:
func init() {
today := time.Now()
// If ENDPOINT is empty, It'll use this hardcoded endpoint. The ENDPOINT variable should not contain any text after "ModifiedDate gt". The actual date is currentDay-1
if ENDPOINT == "" {
ENDPOINT = "http://localhost:8000/Contacts/Export/?$select=Firstname,Lastname,Email,SubaccountId&$filter=EEA eq '' and ModifiedDate gt"
}
// Append CurrentDay-1 in YYY`enter code here`Y-MM-DDTHH:MM:SSZ format.
// The time is NOT in UTC. It's the local time of the machine on which lambda function was running
ENDPOINT = fmt.Sprintf("%s %s", ENDPOINT, today.AddDate(0, 0, -1).Format("2006-01-02T15:04:05Z"))
var err error
// parse the url
PARSED_ENDPOINT, err = url.Parse(ENDPOINT)
if err != nil {
log.Fatalln("Invalid $ENDPOINT", err)
}
// parse the query parameters
parsedQueryParams, err := url.ParseQuery(PARSED_ENDPOINT.RawQuery)
if err != nil {
log.Fatalln("error in parsing query parameters", err)
}
// URLEncode query parameters
PARSED_ENDPOINT.RawQuery = parsedQueryParams.Encode()
}
When I output the URL, I get:
'http://localhost:8000/Contacts/Export/?%24filter=EEA+eq+%27%27+and+ModifiedDate+gt+2018-10-22T08%3A45%3A45Z&%24select=Email%2CFirstname%2CLastname%2CSubaccountId%2CEEA'
How do I return:
'http://localhost:8000/Contacts/Export/?$filter=EEA%20eq%20%27%27%20and%20ModifiedDate%20gt%202018-10-22T00:00:00Z&$select=Email,Firstname,Lastname,SubaccountId,EEA'
Any advice is much appreciated
Golang provide url package to manage this problem and pass the query string with key values to the browser and parse it accordingly after encoding the string which will resolve special characters issue:
package main
import (
"fmt"
"net/url"
)
func main() {
query := make(url.Values)
query.Add("key", "value")
url := &url.URL{RawQuery: query.Encode(), Host: "foo", Scheme: "http"}
fmt.Println(url)
}
Avoid using string query and adding values using fmt package Sprintf methods.This is not a proper way to manage query strings and creating a dynamic url.
Upon getting session information of type map[string]interface{} with this.GetSession("session_key"), I did have to explicitly set the context and type assert the session like this in order to explicitly pass the data to the template.
// Get the session
profile := this.GetSession("profile")
// Have to add data to the template's context
this.Data["nickname"] = profile.(map[string]interface{})["nickname"].(string)
this.Data["picture"] = profile.(map[string]interface{})["picture"].(string)
// Render template
this.TplNames = "user.html"
The session data (type map[string]interface{}) looks like this:
{"nickname": "joe", "picture": "urltotheimg"}
However, according to the Beego's session doc, it looks like the session is passed implicitly without any need of type assertions or context passing (the template has immediate access to session values i.e. {{.nickname}} and {{.picture}})
This is the controller setting the session before redirecting to /user
// Inherit beego's base controller
type MyController struct {
beego.Controller
}
func (this *MyController) Get() {
// code for getting token here
// Getting the User information
client := conf.Client(oauth2.NoContext, token)
resp, err := client.Get("https://" + domain + "/userinfo")
if err != nil {
this.Redirect("/error", 500)
return
}
// Reading the body for user's information
raw, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
this.Redirect("/error", 500)
return
}
// Unmarshalling the JSON of the Profile
var profile map[string]interface{}
if err := json.Unmarshal(raw, &profile); err != nil {
this.Redirect("/error", 500)
return
}
// Saving the information to the session.
this.SetSession("profile", profile)
// redirect to /user
this.Redirect("/user", 301)
}
This is the controller of "/user"
type UserController struct {
beego.Controller
}
func (this *UserController) Get() {
// get the saved session
profile := this.GetSession("profile")
// without setting the template data here, the session data won't be
// available in user.html
this.Data["nickname"] = profile.(map[string]interface{})["nickname"].(string)
this.Data["picture"] = profile.(map[string]interface{})["picture"].(string)
this.TplNames = "user.html"
}
Only this then I can map the template to the data like this:
<img src="{{ .picture }}">
<p>Hello, {{ .nickname }}</p>
I'm quite sure it's necessary to set the template data. I'm just not sure why the above doc didn't do that.
Any help would be appreciated.
I just tried running the Beego quickstart project and ran it successfully.
Make sure you have both the beego and bee installed. After creating a new project with bee new projectname make sure you edit the projectname/conf/app.conf file and add the sessionon = true:
appname = quickstart
httpport = 8080
runmode = dev
sessionon = true
I created a redirect controller like:
type RedirectController struct {
beego.Controller
}
func (c *RedirectController) Get() {
profile := make(map[string]interface{})
profile["nickname"] = "User's Nickname"
profile["picture"] = "/path/to/img.jpg"
c.SetSession("profile", profile)
c.Redirect("/", 301)
}
The main controller:
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
profile := c.GetSession("profile")
c.Data["nickname"] = profile.(map[string]interface{})["nickname"]
c.Data["picture"] = profile.(map[string]interface{})["picture"]
c.TplNames = "index.tpl"
}
My index.tpl file:
<p>Nickname: {{.nickname}}</p>
<p>Picture: {{.picture}}</p>
And the router:
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/redirect", &controllers.RedirectController{})
}
I would also recommend you to use a structure to store the profile values like:
// Define struct.
type Profile struct{
Nickname string
Picture string
}
// Save it for template rendering.
this.Data["profile"] = &Profile{Nickname:"astaxie", Picture:"img.jpg"}
// And render it like this:
Nickname: {{.profile.Nickname}}
Picture: {{.profile.Picture}}
Make sure to read this to understand how template rendering is done. I hope this is what you were asking for, if not, please edit your question and add more helpful information and I will edit this answer.
Im using web.go (http://webgo.io/) for writing a simple web app that accepts json in a POST request and after parsing it returns the result. Im having trouble reading the json from ctx.Params object.
Below is the code i have so far
package main
import (
"github.com/hoisie/web";
"encoding/json"
)
func parse(ctx *web.Context, val string) string {
for k,v := range ctx.Params {
println(k, v)
}
//Testing json parsing
mapB := map[string]int{"apple": 5, "lettuce": 7}
mapD, _ := json.Marshal(mapB)
return string(mapD)
}
func main() {
web.Post("/(.*)", parse)
web.Run("0.0.0.0:9999")
}
Though the post request gets registered i dont see anything printed on the command line for the json i posted. How can i fix this ?
Thank You
The reason you're not getting any JSON data from the body of the POST request is because hoisie/web reads form data into .Params, as seen here:
req.ParseForm()
if len(req.Form) > 0 {
for k, v := range req.Form {
ctx.Params[k] = v[0]
}
}
In order to fix this, you'll need to add something that can parse the raw body of the response. You should just be able to use ctx.Body to access the raw body, since it implements *http.Request and doesn't redefine Body in the Context struct.
For example, this should work:
json := make(map[string]interface{})
body, err := ioutil.ReadAll(ctx.Body)
if err != nil {
// Handle Body read error
return
}
err = json.Unmarshal(body, &json)
if err != nil {
// Handle JSON parsing error
return
}
// Use `json`