Handle array of ids in a request using gorilla/mux - go

I need to handle such a request using gorilla/mux:
/objects?id=JDYsh939&id=OYBpo726
As I understood while reading the documentation, I can specify a pattern like this: {name:pattern} but I don't know if it's would work to specify that the url will contain several times the id parameter.
Any ideas?

You do not need to specify the parameter for that as the query string parameters go into the corresponding collection of the HttpRequest.
The following code shows how to handle them:
r.HandleFunc("/objects", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello! Parameters: %v", r.URL.Query())
})
See https://golang.org/pkg/net/url/#pkg-examples on how to deal with URL query string parameters.

Related

Json response in Go(Gin) without escape characters

I have recently started working on Go APIs using GIN. My API is getting the data from DB with two columns where one column contains integer and other contains a json string.
The json string is dynamic and hence i cant use struct for that.
I am using map[string]interface{} to parse the json and modify it and then parse it back to json using json.Marshal. Now i am returning this json string as a response but getting escape characters. Have done some search regarding that, but didnt find any solution yet.
Here is the part of code that i am using
var interface_obj map[string]interface{}
json.Unmarshal([]byte(grants.Data), &interface_obj)
grants_map := interface_obj["role_grants"].(map[string]interface{})
jsonString, err := json.Marshal(grants_map)
jsonBody := string(jsonString)
After this, I am returning this JSON as response in GIN framework like this
c.JSON(http.StatusCreated, gin.H{"message": "Json retrieved successfully", "data": jsonBody})
But the output i am getting is
{
"data": "[{\"action\":\"read\",\"resource\":\"project\"},{\"action\":\"all\",\"resource\":\"users\"},{\"action\":\"all\",\"resource\":\"roles\"},{\"action\":\"all\",\"resource\":\"project-settings\"},{\"action\":\"create\",\"resource\":\"single-entity-screening\"},{\"action\":\"read\",\"resource\":\"single-entity-screening\"},{\"action\":\"create\",\"resource\":\"multi-batch-screening\"},{\"action\":\"read\",\"resource\":\"multi-batch-screening\"},{\"action\":\"read\",\"resource\":\"workspace\"},{\"action\":\"allocate\",\"resource\":\"workspace\"},{\"action\":\"update\",\"resource\":\"workspace\"},{\"action\":\"read\",\"resource\":\"case\"},{\"action\":\"allocate\",\"resource\":\"case\"},{\"action\":\"review\",\"resource\":\"case\"},{\"action\":\"update\",\"resource\":\"case\"},{\"action\":\"read\",\"resource\":\"report\"},{\"action\":\"read\",\"resource\":\"audit-trail\"},{\"action\":\"read\",\"resource\":\"delivery\"}]",
"message": "Grants retrieved successfully"
}
I printed it on my console and it looked fine there, but causing this issue on response.
Is there any way to resolve this using some standard way? Please guide
Thanks
You don't need to do json.Marshal(grants_map), just pass the value directly to gin.H and let c.JSON do the encoding, i.e.
gin.H{... "data": grants_map}
And in cases where you truly have raw JSON data at hand that you want to send as part of other not-yet-JSON data, you can wrap it into json.RawMessage to avoid the "double-encoding", i.e.
gin.H{... "data": json.RawMessage(jsonBody)}

Programmatically set an url parameter in gin context for testing purpose

I am writing some test suites for gin middlewares. I found a solution to test them without having to run a full router engine, by creating a gin context like this :
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
The goal is to test my function by calling :
MyMiddleware(c)
// Then I use c.MustGet() to check if every expected parameter has been transmitted to gin
// context, with correct values.
One of my middlewares relies on c.Param(). Is it possible to programatically set an Url param in gin (something like c.SetParam(key, value)) before calling the middleware ? This is only for test purpose so I don't mind non-optimized solutions.
Finally figured it out by using IntelliJ to inspect the structure, I can just set it the raw way :
c.Params = []gin.Param{
{
Key: "id",
Value: "first document",
},
}
I was not able to get the accepted answer to work due to the c.Request.URL being nil in some of my tests.
Instead, you can set the query like this:
c.Request.URL, _ = url.Parse("?id=mock")

Non-escaped query parameters for URL

I want to create a query parameter without escaping the query string.
For example, I need to create a query parameter q with the content
"before:{2019-12-24 19:57:34}"
so that the URL is
https://android-review.googlesource.com/changes/?q=before:{2019-12-24 19:57:34}
If I use this code (Golang Playground)
url, _ := url.Parse("https://android-review.googlesource.com/changes/")
q := url.Query()
q.Set("q", "before:{2019-12-24 19:57:34}")
url.RawQuery = q.Encode()
fmt.Println(url)
url is escaping the special characters, spaces, and brackets:
https://android-review.googlesource.com/changes/?q=before%3A%7B2019-12-24+19%3A57%3A34%7D
How can I solve this issue except manually creating the URL (without query parameters then)?
If you don't want your URL query to be encoded, then don't use the Encode() method. Instead, set the RawQuery value directly, yourself:
url, _ := url.Parse("https://android-review.googlesource.com/changes/")
url.RawQuery = "q=before:{2019-12-24 19:57:34}"
fmt.Println(url)
Output:
https://android-review.googlesource.com/changes/?q=before:{2019-12-24 19:57:34}
playground
Keep in mind, however, that this is a recipe for potential disaster, depending on how that url is eventually used. In particular, the space in that URL should be escaped, according to RFC. See more here.
Perhaps you'll want to implement your own minimal escaping, if that's compatible with your use-case.

go properly handling slices and strings

I am using goRequest http://parnurzeal.github.io/gorequest/ to make some HTTP requests against a server process I need to talk to. The authentication process works like this;
send in a GET request with an authentication header set. No problem there, but I need to grab a header from the response and use a returned value to reauthenticate each following request.
The retuned HTTP header looks like this.
Response headers
map[Location:[900767244] Content-Type:[application/xml] Date:[Fri, 18 Sep 2015 18:19:41 GMT] Server:[Apache] X-Frame-Options:[SAMEORIGIN] Set-Cookie:[JSESSIONID=D5C976F5646365FF030DBAD770DA774C; Path=/; Secure; HttpOnly]]
I need to get that location value as it's my session token going forward. If I grap it like this:
session, ok := response.Header["Location"]
if !ok {
fmt.Println("Did not receive a location header.")
}
fmt.Println("Session: ", session)
I can get it, but it's a slice and NOT a string. How can I get that value as a string so I can pop it back into my request headers going forward? As you can see in the following error:
./login.go:133: cannot use session (type []string) as type string in argument to logoutRequest.Delete
Thanks a lot!
Craig
If you want one value, use the Header's Get method
location := response.Header.Get("Location")
This also canonicalizes the header name for you, so that you still get a value even when using slightly different capitalization.
You only need to index an http.Header value directly when you need to get more than than the first possible value. If you want all values with a canonicalized header name, you can use textproto.CanonicalMIMEHeaderKey
vals := response.Header[textproto.CanonicalMIMEHeaderKey(header)]
The headers have location as an array, you just need to pass Location[0] to that method rather than simply Location because it is a slice. That being said, indexing into the array without checking the bounds is unsafe so you should probably do;
if len(Location) == 1 {
logoutRequest(Location[0])
} else {
// error state
}
One last thing to provide you guidance in the future. You can tell that the response headers object has a type more like this; map[string][]string (in reality that maybe []interface{} but I'm not certain from the output) by seeing that each item inside the outer brackets [] has it's values contained within another set of brackets, meaning it is an array that could contain 0 or more values.

GoREST endpoint path

I'm writting a web service with Go and I'd like to have url like :
http://example.com/WEB/service.wfs?param1=2&param2=test.....
I'm using GoREST and my Endpoint url is :
method:"GET" path:"/WEB/service.wfs?{param:string}" output:"string"
My problem is that it never return the "param" but it does if I use the endpoint :
method:"GET" path:"/WEB/service.wfs/{param:string}" output:"string"
Is there a way to handle the "?" ?
You can do this in gorest though it's not as nice as gorest's preferred mechanism.
Don't include your query parameters in your endpoint definition
method:"GET" path:"/WEB/service.wfs" output:"string"
Instead, you can get at the context from your registered end point and get the query parameters using something like
func (serv MyService) HelloWorld() (result string) {
r := serv.Context.Request()
u, _ := url.Parse(r.URL.String())
q := u.Query()
result = "Buono estente " + q["hi"][0]
return
}
I have had a look at the GoREST package you are using and can not see any way of doing this.
I have always used gorillatoolkit pat package.
gorillatoolkit
There is an example of what you want to do about half way down.
category := req.URL.Query().Get(":category")
This way you can get the query parameters on the request URL by the key.
Hope this helps.

Resources