I want to retrieve a value from a URL. Provided I have a URL such as http://myurl.com/theValue1/iWantToRetrieveThis, I want to split this value and want to retrieve theValue1 and iWantToRetrieveThis. How can I do this?
I tried the code below but it seems that it's only retrieving the query string:
func decodeGetTokenRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
fmt.Println("decoding here", path.Base(r.URL))
return getTokenRequest{
SellerID: r.URL.Query().Get("sellerid"), <<--- THis is empty
Scope: r.URL.Query().Get("scope"), <<-- This is also empty
Authorization: Validation{
credential: r.Header.Get("ETM-API-AUTH-KEY"),
},
}, nil
}
Ok I just had to go with #Rafal answer since what I'm trying to retrieve is not a query parameter but part of the url.
Related
I have a working REST API based on this API Gateway tutorial. I'm able to successfully invoke it via the test functionality of the AWS Console; and I'm able to successfully invoke it via my simple iOS Swift 4.2 Xcode application using the iPhone XR simulator.
I know it's working via a real, live external call because I can see the Cloudwatch logs which always register a 200 response and is sending the results back to the Client.
My problem is really in understanding the Swift code, and I'm hoping that a Swift expert can help me understand how to unpack result in the code below.
Here's my code in ViewController.swift for invoking the REST API and attempting to print result to the console:
#IBAction func userInvokeApi(_ sender: UIButton) {
print("You clicked invoke api...")
let client = SVTLambdaGateClient.default()
client.calcGet(operand2: "3", _operator: "+", operand1: "5").continueWith{ (task: AWSTask?) -> AnyObject? in
if let error = task?.error {
print("Error occurred: \(error)")
return nil
}
if let result = task?.result {
// Do something with result
print("The result is... \(result)")
}
return nil
}
}
As pointed out in the comments below, I'm getting the following result because it's printing out the address of the object:
You clicked invoke api...
The result is... <AmplifyRestApiTest.Empty: 0x600002020770> {
}
(where AmplifyRestApiTest is the name of my Xcode project.)
UPDATE When I set a breakpoint on the print statement, this is what I see in the Debug pane:
UPDATE 2
When I type task?.result there are two viable properties as per this answer from the Amplify team: error and result. So, since my API responds successfully I am assuming I just don't know how to view result.
Can someone help me understand what steps I must take to access members of this class object?
Here is the corresponding method in the API Gateway-generated iOS Swift SDK code:
/*
#param operand2
#param _operator
#param operand1
return type: Empty
*/
public func calcGet(operand2: String, _operator: String, operand1: String) -> AWSTask<Empty> {
let headerParameters = [
"Content-Type": "application/json",
"Accept": "application/json",
]
var queryParameters:[String:Any] = [:]
queryParameters["operand2"] = operand2
queryParameters["operator"] = _operator
queryParameters["operand1"] = operand1
let pathParameters:[String:Any] = [:]
return self.invokeHTTPRequest("GET", urlString: "/calc", pathParameters: pathParameters, queryParameters: queryParameters, headerParameters: headerParameters, body: nil, responseClass: Empty.self) as! AWSTask<Empty>
}
I'm fairly certain this return type of Empty refers to the Empty model defined for the REST API as shown in the screenshot below. I think it's "empty" because the API doesn't alter the response from the Lambda function back to the Client. So, it's all pass-through. Indeed, the tutorial explains that the other models -- Output and Result -- are not used because it "relies on the passthrough behavior and does not use this model."
Any thoughts?
I am using fetch API to send two values to my POST request handler...
fetch('http://localhost:8080/validation', {
method:'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email:this.state.email,
password:this.state.password
})
I want to save both email and password as strings on the server side. Here is my attempt...
type credentials struct {
Test string
}
func Validate(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
decoder := json.NewDecoder(req.Body)
var creds credentials
err := decoder.Decode(&creds)
if err != nil {
panic(err)
}
fmt.Println(creds.Test)
}
The problem is I do not know how exactly the format of the structure being sent to the POST. I am attempting to save req.Body as a string but this yields nothing.
When I print fmt.Println I get a blank space. What is the proper way of parsing it?
Try with
type credentials struct {
Email string `json:"email"`
Password string `json:"password"`
}
You are receiving a JSON with two values. Receiving struct should have a structure matching your request. Otherwise, there are no placeholders to decode the JSON into, as in your case - email and password do not have matching struct fields. Btw. if you send "Test" in your JSON, this would work, as you have a Test field in your struct!
Regarding field names. If fields in JSON do not start with a capital letter or even have different names, then you should use so called tags.
More on tags: https://golang.org/pkg/encoding/json/#Marshal
In my example I used them to match struct field names to your json fields, i.e. to make email from json match Email field of the credentials struct.
req.Body is an io.Reader, and you can get use ioutil.ReadAll to drain it:
data, err := ioutil.ReadAll(req.Body)
asString := string(data) // you can convert to a string with a typecast
But I'm not sure if that's what you meant by trying to save req.Body as a string.
To parse the response into a data structure, you can unmarshal it into a variable of type *interface{}:
var creds interface{}
decoder.Decode(&creds)
And then examine the value:
fmt.Printf("%#v\n", creds)
Or perhaps using pp.Println(creds) which I find easier to read.
The creds variable will represent the JSON object found in the body, for your example input this will be a map[string]interface{} with two entries, presumably both of them strings. Something like:
map[string]interface{}{
"email": email_value,
"password": password_value,
}
and you check the values with:
email, ok := creds["email"].(string)
if ok {
// email will contain the value because creds["email"] passed the type check
fmt.Printf("submitted email is %#v\n", email)
} else {
// "email" property was a string (may be missing, may be something else)
}
The documentation of json.Unmarshal explains the semantics of how arbitrary JSON strings can be parsed without knowing their structure in advance in the discussion about unmarshalling to interface values.
Im trying to make a GET request to Foursquare's Photos From a Venue and
Foursquare's Explore at the same time. Right now (correct me if I'm wrong) I have two methods to make the request via Alamofire and convert the response to a JSON object using SwiftyJSON.
I can successfully update the UITableViewCell's labels to reflect the data using makeRequest() below, but can't update the UIImage for the background photo of each respective cell.
My problem is A) getting a usable photo URL, and B) Initializing "pin" while providing data from two separate requests. In order to initialize pin, I need to set all of the values. Im geting 90% of the values from one request, and 10% (the photo URL that I need to get) from another request. How do I initialize "pin" with data from two separate requests?
makeImageRequest:
func makeImageRequest() {
let venueID = "43695300f964a5208c291fe3"
let firstURL = "https://api.foursquare.com/v2/venues/\(venueID)/photos"
Alamofire.request(.GET, firstURL, parameters: [
"client_id" : foursquareClientID,
"client_secret" : foursquareClientSecret,
"v" : "20140806",
"m" : "foursquare",
"limit" : "10"
])
.responseJSON { (request, response, data, error) in
println(request)
println(response)
println(error)
let jsonObj = JSON(data!)
self.pins = []
for obj in jsonObj["response"]["photos"]["items"].arrayValue {
let photoURL = obj["prefix"]["suffix"].stringValue
println("the photo url is\(photoURL)")
let pin = Pin(title: obj["venue"]["name"].stringValue, openUntil: obj["venue"]["hours"]["status"].stringValue, address: obj["venue"]["location"]["address"].stringValue, ratingSignals: obj["venue"]["ratingSignals"].stringValue, ratingImage: UIImage(named:"Score8-5")!, venueImage: UIImage(named: "FloraGrubTestImage.jpg")!)
self.pins.append(pin)
}
self.tableView.reloadData()
}
}
and makeRequest
func makeRequest(searchString: String) {
let secondURL = "https://api.foursquare.com/v2/venues/explore"
Alamofire.request(.GET, secondURL, parameters: [
"client_id" : foursquareClientID,
"client_secret" : foursquareClientSecret,
"ll" : "37.71987,-122.470089",
"query" : searchString,
"radius" : "1000",
"limit" : "10",
"v" : "20140806",
"m" : "foursquare"
])
.responseJSON { (request, response, data, error) in
println(request)
println(response)
println(error)
let jsonObj = JSON(data!)
self.pins = []
println(jsonObj)
for obj in jsonObj["response"]["groups"][0]["items"].arrayValue {
let pin = Pin(title: obj["venue"]["name"].stringValue, openUntil: obj["venue"]["hours"]["status"].stringValue, address: obj["venue"]["location"]["address"].stringValue, ratingSignals: obj["venue"]["ratingSignals"].stringValue, ratingImage: UIImage(named:"Score8-5")!, venueImage: UIImage(named: "FloraGrubTestImage.jpg")!)
self.pins.append(pin)
}
self.tableView.reloadData()
}
}
I have a separate bindData() method in my UITableViewCell class. Does this make sense? Can anyone help me?
UPDATE: An engineer I work with suggested that I make another request inside of the same method makeRequest and not bother with breaking it out into two separate methods. I also read a tutorial online that suggests a response router of some kind. Any suggestions on how I can refactor this code into one method?
UPDATE #2: I have renamed this question as I realize that the original question was not my real problem
The easiest way would be to execute these request serially. First retrieve all the pins, and then retrieve all the photos. This is essentially what the other engineer advised.
If you fire off these requests in parallel, it will require a extra work to merge the responses, since you don't know which request will return first.
Now to merge the pins you need a way to uniquely identify each pin. If they have some ID, you can use that. Otherwise you have to rely on the sort order and index. Assuming each request returns the same pins in the same order.
After the first request returns, you have an array of pins. Then in the second request callback you can retrieve the matching pin from this array and update it with the new data.
I am trying to pass in a token to the "Parse(token String, keyFunc Keyfunc)" GO routine defined in this GO-library (http://godoc.org/github.com/dgrijalva/jwt-go) for JWT-token parsing/validation.
When I pass the token to this function -
token, err := jwt.Parse(getToken, func(token *jwt.Token) (interface{}, error) {
return config.Config.Key, nil
})
I get an error which says "Key is invalid or invalid type".
My config struct looks like this in config.go file -
config struct {
Key string
}
Any suggestions to solve this problem? The token I am passing is a JWT token.
config struct {
Key string
}
Key needs to be a []byte
I am not sure if this can be an issue for someone else.
My problem was I was using Signing Method "SigningMethodES256" but "SigningMethodHS256" or Any with SigningMethodHS* works fine.
If someone knows why this is an issue please answer.
Other way is to do something like this -
token, err := jwt.Parse(getToken, func(token *jwt.Token) (interface{}, error) {
return []byte(config.Config.Key), nil
})
The whole idea being that the Parse function returns a slice of bytes.
Taking a look at the function signatures in the GoDoc for github.com/dgrijalva/jwt-go we see:
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error)
type Keyfunc func(*Token) (interface{}, error)
Keyfunc requires you to return (interface{}, error). Given the mysterious interface{} type, you might expect to be fine returning a string; however, a peek under the hood reveals that Parse() tries to Verify(), which attempts the following type assertion with your interface{} value as the key:
keyBytes, ok := key.([]byte)
That will succeed for []byte types, but will fail for string types. When that fails, the result is the error message you are getting. Read more about type assertions in the Effective Go documentation to learn why it fails.
Example: https://play.golang.org/p/9KKNFLLQrm
package main
import "fmt"
func main() {
var a interface{}
var b interface{}
a = []byte("hello")
b = "hello"
key, ok := a.([]byte)
if !ok {
fmt.Println("a is an invalid type")
} else {
fmt.Println(key)
}
key, ok = b.([]byte)
if !ok {
fmt.Println("b is an invalid type")
} else {
fmt.Println(key)
}
}
[104 101 108 108 111]
b is an invalid type
This is working for me.
token.SignedString([]byte("mysecretkey"))
func GenerateJWT(email string, username string) (tokenString string, err error) {
expirationTime := time.Now().Add(1 * time.Hour)
claims := &JWTClime{
Email: email,
Username: username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err = token.SignedString([]byte("mysecretkey"))
return
}
I am trying to use Alamofire to use the Google Translate api.
Here is the code I am trying to use:
let textToTranslate = "你好"
let parameters = ["key":"MY_PRIVATE_IOS_APP_KEY","q":"\(textToTranslate)",
"source":"zh-CN","target":"en"]
Alamofire.request(.GET, "https://www.googleapis.com/language/translate/v2/languages", parameters:parameters)
.responseJSON
{ (_, _, JSON, _) -> Void in
let translatedText: String? = JSON?.valueForKeyPath("data") as String?
println(translatedText)
}
The console keeps printing nil.
Also, in the Google Translate API Usage Report it says I have "Client Errors".
This is the kind of output I get when I try this in the browser:
{
"data": {
"translations": [
{
"translatedText": "hello"
}
]
}
}
What am I missing or doing wrong?
The URL that you should GET is:
https://www.googleapis.com/language/translate/v2?parameters
On your code I see the URL:
https://www.googleapis.com/language/translate/v2/languages?parameters