Golang Get all POST Form data using PostParams and get values as string - go

I want to get all the post form data and get the values as string, however using PostParams I am able to get all the post data, but the values are as array key: [value], how can I get all the form data as string values?
[Edit]
I may not stated clearly, but I need to get the whole form as JSON, because I need to send the form data to another API which need the body as JSON data
form.html
<form>
<input type='text' name='key' />
<input type='text' name='key2' />
</form>
script.js
$.ajax( {
url: '/post/action',
type: 'POST',
data: new FormData(this),
processData: false,
contentType: false
}).done(function(r) {
console.log(r);
});
action.go
func PostAction(c echo.Context) error {
form, _ := c.FormParams()
b, _ := json.Marshal(form)
log.Println(b) // this will print: key: [value], key2: [value]
// I want to get value as string: key: value, key2: value
// ..other function, return, etc
}
How can I get the form values as string?
Or is there another function that does this?
Thank you very much in advance

No need to JSON marshal the already decoded form, simply do:
form["key"][0]
form["key2"][0]
See the docs on c.FormParams and then click on the return type to see its documentation, and you'll notice that it's just the stdlib's url.Values type, which is defined as a map of string slices (i.e., map[string][]string).
If you need to convert a value of type url.Values to a "simple object", you can convert it to a map[string]string using the following code:
o := make(map[string]string, len(form))
for k, v := range form {
// if you are certain v[0] is present
o[k] = v[0]
// if you are not sure v[0] is present, check first
if len(v) > 0 {
o[k] = v[0]
}
}
data, err := json.Marshal(o)
if err != nil {
return err
}
// ...

Related

Create an array of a certain type and reuse it

I'm having trouble creating the payload for my graphql resolver. How could I rewrite this to return a completed array?
I'm stuck inside of c.OnHTML("article", func(e *colly.HTMLElement) {} and can't return the data outside of it.
type Article struct {
Title string `bson:"title"`
Source string `bson:"source"`
Url string `bson:"url"`
Timestamp string `bson:"timestamp"`
}
func (r *RootResolver) News() ([]models.Article, error) {
c := colly.NewCollector(
colly.MaxDepth(2),
colly.Async(),
)
c.Limit(&colly.LimitRule{Parallelism: 10})
articles := []models.Article{}
c.OnHTML("article", func(e *colly.HTMLElement) {
articleModel := []models.Article{
{
Title: e.ChildText("h3 a"),
Source: e.ChildText("a[data-n-tid]"),
Timestamp: e.ChildAttr("div:last-child time", "datetime"),
Url: e.ChildAttr("a[href]", "href"),
},
}
fmt.Println(articleModel)
})
c.Visit(SOMEURLHERE)
c.Wait()
return articles, nil
}
If you need to get full list of articles, you need to append new article into articles slice inside of your c.OnHTML function instead creating new slice.
and return articles slice end of the News() Method.
articles = append(articles, models.Article{
Title: e.ChildText("h3 a"),
Source: e.ChildText("a[data-n-tid]"),
Timestamp: e.ChildAttr("div:last-child time", "datetime"),
Url: e.ChildAttr("a[href]", "href"),
})
In your current implementation, articles := []models.Article{} creates an empty slice of models.Article type. And inside c.OnHTML, you creating another slice of models.Article type named articleModel with and element. That two slices has no any connection. So you need to append new element to your articles slice and return it with articles.

Request body validation with http Mux

I am trying to build an API using Go and Mux. I am trying to validate the incoming requests. I tried using tags on Struct and validated it using Go Validator.
This is my Struct
type Address struct {
Street string `validate:"required"`
City string `validate:"required"`
Phone string `validate:"required"`
}
My issue is
There is a POST endpoint where all the fields would be passed and the validation would not fail. However there is another PATCH endpoint where not all fields of Address struct would be passed and hence the validation is failing.
eg. API might receive
{
"Street": "Dummy"
}
What is the best way to validate both POST and PATCH request in this scenario.
In the PATCH endpoint use a different type with different (if any) validation tags. Convert to Address afterward:
// Same underlying type as Address, but no validation tags
var x struct {
Street string
City string
Phone string
}
// Decode request into x, then:
addr := Address(x)
Change my answer
I've been tested on your case and found the solution, you must use struct level validation. Here is the my function on your case:
func PostRequestValidation(sl validator.StructLevel) {
address := sl.Current().Interface().(Address)
jsonMarshal, _ := json.Marshal(address)
var m map[string]interface{}
_ = json.Unmarshal(jsonMarshal, &m)
for key, val := range m {
if len(fmt.Sprintf("%s", val)) == 0 {
sl.ReportError(val, key, key, "post", "")
}
}
}
func PutRequestValidation(sl validator.StructLevel) {
address := sl.Current().Interface().(Address)
isValid := false
jsonMarshal, _ := json.Marshal(address)
var m map[string]interface{}
_ = json.Unmarshal(jsonMarshal, &m)
for _, val := range m {
if len(fmt.Sprintf("%s", val)) > 0 {
isValid = true
}
}
if !isValid {
sl.ReportError(address, "address", "Adress", "put", "")
}
}
You just have to register on each validation request
// ON POST REQUEST
validate = validator.New()
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
validate.RegisterStructValidation(PostRequestValidation, Address{})
// ON PUT REQUEST
validate = validator.New()
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
validate.RegisterStructValidation(PutRequestValidation, Address{})
You may copy from my source code for more detailed source code.
Found this library - govalidator. This allows to create rules for each request based on the requirement. For POST request, create a set of rules with all the fields as mandatory. For PATCH request, create rules with all fields as optional.

Type assertion for TypeList in terraform provider

I'm writing a Terraform provider and I'm trying to figure out the best way to do type assertion when I have a TypeList containing elements of TypeString.
The resouce is defined as follows:
return &schema.Resource{
Create: resourceConfigObjectCreate,
Read: resourceConfigObjectRead,
Update: resourceConfigObjectUpdate,
Delete: resourceConfigObjectDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"notification_options": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: schema.Schema{
Type: schema.TypeString,
},
},
},
}
}
And i would like to load those values to a custom type defined like that:
type ConfigObject struct {
Name string `json:"name,omitempty"`
NotificationOptions []string `json:"notification_options,omitempty"`
}
Since schema.ResourceData.Get returns an interface{} a type assertion is needed.
item := thruk.ConfigObject{
Name: schema.ResourceData.Get("name").(string),
NotificationOptions: extractSliceOfStrings(schema.ResourceData.Get("notification_options")),
}
I've done it easily for string but the slice of strings was more complex and I created the following function:
func extractSliceOfStrings(i interface{}) (slice []string) {
s := reflect.ValueOf(i)
if !s.IsValid() {
return
}
for i := 0; i < s.Len(); i++ {
slice = append(slice, s.Index(i).String())
}
return
}
Is this the correct approach?
When working with the ResourceData API in a Terraform provider, it's helpful to know which Go type corresponds to each of the schema types. You've already inferred that schema.TypeString corresponds to string. Here's a complete list:
TypeBool ⇒ bool
TypeString ⇒ string
TypeInt ⇒ int
TypeList ⇒ []interface{}
TypeMap ⇒ map[string]interface{}
TypeSet ⇒ *schema.Set
Element type when Elem is set to a *schema.Resource: map[string]interface{}
The translations above are documented on the Schema Types documentation page for the SDK, as "Data structure:" under each of the headings.
Whenever you are dealing with a collection, the element type from Go's perspective is always interface{} to reflect the fact that the element type isn't decided until runtime. However, the same mapping rules defined above apply to those element values too, and so to convert a TypeList whose Elem is a TypeString you'd first assert the slice type, and then assert each element in turn:
itemsRaw := d.Get("example").([]interface{})
items := make([]string, len(itemsRaw))
for i, raw := range itemsRaw {
items[i] = raw.(string)
}
Unfortunately there is no way to go directly from []interface{} to []string in a single step, due to the design of Go interfaces and type assertions.
You can take a similar approach for TypeMap, if you ultimately need map[string]string:
itemsRaw := d.Get("example").(map[string]interface{})
items := make(map[string]string, len(itemsRaw))
for k, raw := range itemsRaw {
items[k] = raw.(string)
}
TypeSet is a little more complicated due to the custom *schema.Set container, but you can call the List method of the set to obtain a []interface{} which you can then treat the same as with TypeList above:
itemsRaw := d.Get("example").(*schema.Set).List()
items := make([]string, len(itemsRaw))
for i, raw := range itemsRaw {
items[i] = raw.(string)
}

Golang, Ajax - How to return slices or struct in a success function?

My problem is similar to that of the question in this link. I need to return multiple slices or a struct from golang to the ajax success block. I tried to marshal my slice into JSON but it is received in ajax as a string. I need to receive it as an array. Is it possible to send multiple arrays or a struct like this?
My code:
b, _ := json.Marshal(aSlice) // json Marshal
c, _ := json.Marshal(bSlice)
this.Ctx.ResponseWriter.Write(b) // Beego responsewriter
this.Ctx.ResponseWriter.Write(c)
My ajax:
$.ajax({
url: '/delete_process',
type: 'post',
dataType: 'html',
data : "&processName=" + processName,
success : function(data) {
alert(data);
alert(data.length)
}
});
Thanks in advance.
The dataType parameter of your ajax request should be json as you are expecting JSON data from the server. But if you server does not respond with valid JSON the ajax request is going to result in an error. Check your browser's javascript console for errors.
From what you are currently doing in the controller, its definitely going to result in invalid JSON response. See following.
aSlice := []string{"foo", "bar"}
bSlice := []string{"baz", "qux"}
b, _ := json.Marshal(aSlice) // json Marshal
c, _ := json.Marshal(bSlice)
this.Ctx.ResponseWriter.Write(b) // Writes `["foo","bar"]`
this.Ctx.ResponseWriter.Write(c) // Appends `["baz","qux"]`
This results in sending ["foo","bar"]["baz","qux"] Thats just two JSON array strings appended together. Its not valid.
What you probably want to send to browser is this: [["foo","bar"],["baz","qux"]].
That is an array of two arrays. You can do this to send it from the server.
aSlice := []string{"foo", "bar"}
bSlice := []string{"baz", "qux"}
slice := []interface{}{aSlice, bSlice}
s, _ := json.Marshal(slice)
this.Ctx.ResponseWriter.Write(s)
And in the javascript side,
$.ajax({
url: '/delete_process',
type: 'post',
dataType: 'json',
data : "&processName=" + processName,
success : function(data) {
alert(data);
alert(data[0]); // ["foo","bar"]
alert(data[1]); // ["baz","qux"]
alert(data.length) // 2
}
});
The answer of #AH works really well for multiple slices. Now if you want a Struct you should change a little bit your code :
package controllers
import "github.com/astaxie/beego"
import "encoding/json"
type Controller2 struct {
beego.Controller
}
type Controller2Result struct {
Accommodation []string
Vehicle []string
}
func (this *Controller2) Post() {
var result Controller2Result
aSlice := []string{"House", "Apartment", "Hostel"}
bSlice := []string{"Car", "Moto", "Airplane"}
result.Accommodation = aSlice
result.Vehicle = bSlice
s, _ := json.Marshal(result)
this.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json")
this.Ctx.ResponseWriter.Write(s)
}
Ajax
$.ajax({
url: '/Controller2',
type: 'post',
dataType: 'json',
//data : "&processName=" + processName,
success : function(data) {
alert(JSON.stringify(data));
}
});
How is explained here alert only can display strings and datais a object type of JavaScript. So you have to use JSON.stringify to turn object into JSON-String.

Getting the URL parameter in Go issue

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

Resources