I need to develop a small Rails app that makes a request to an JSON API, introducing the parameters into an initial form, check if we get a real response and then render the results into a view (html.erb).
Do you know where can I get good material to do these steps? Any help is welcome.
I'm reading some near example:
params_string = "whatever"
params_string_with_api_key = params_string + "&" + ##API_KEY
hashkey = Digest::SHA1.hexdigest(params_string_with_api_key)
params_string += "&hashkey=#{hashkey}"
res = Net::HTTP.get_response("api.somecompany.com", "/some/url.json?#{params_string}")
res_sha1 = Digest::SHA1.hexdigest(res.body + ##API_KEY)
#verified = res["X-Somecompany-Response-Signature"] == res_sha1
parsed_json = ActiveSupport::JSON.decode(res.body)
#results = parsed_json["results"]
Is it always needed to encode the parameters string when you do the Net::HTPP request? Is there another way?
What does exactly params_string += "&hashkey=#{hashkey}"?
Thank you!
What does exactly params_string += "&hashkey=#{hashkey}"?
params_string is a string that looks like ?param1=val¶m2=val2.... Your last piece of code is just appending another param to the string. If your issue is with the #{} fragment, this syntax, in a ruby double-quoted string, allows you to use the value of a var.
Is it always needed to encode the parameters string when you do the Net::HTPP request? Is there another way?
I don't see the parameters string being encoded here. All I see is a checking of the results, done by comparing a response header with a SHA1'd response body.
Not really related to your questions : I went away from Net::HTTP a while back, having troubles with segfault. I now use Typhoeus for all requests through the network.
Related
I use Rest Assured framework (Java).
I need to send integer array as http-param in get request: http://example.com:8080/myservice?data_ids=11,22,33
Integer[] ids = new Integer[] {11, 22, 33};
...
RequestSpecificationImpl request = (RequestSpecificationImpl)RestAssured.given();
request.baseUri("http://example.com");
request.port(8080);
request.basePath("/myservice");
...
String ids_as_string = Arrays.toString(ids).replaceAll("\\s|[\\[]|[]]", "");
request.params("data_ids", ids_as_string);
System.out.println("Params: " + request.getRequestParams().toString());
System.out.println("URI" + request.getURI());
What I see in the console:
Params: {data_ids=11,22,33}
URI: http://example.com:8080/myservice?data_ids=11%2C22%2C33
Why do my commas transform into '%2C'?
What needs to be done to ensure that commas are passed as they should?
Disable URL encoding, simple as that
given().urlEncodingEnabled(false);
Official documentation
Verified locally,
I have developed a REST API using feathers.js (https://feathersjs.com/).
When trying to do a HTTP 'read' request in Flutter using package:http/http.dart I have encountered an error. The http.dart library is unable to correctly parse the query params I pass to the URI.
The error I receive through the Android Studio debug console is ;
FormatException: Invalid character (at character 84) E/flutter
(11338): ...lk.com/weatherobs?last=true&location[$in]=Bellambi&location[$in]=Nowra ....
The error is indicating the square brackets and possibly the $ sign ('[$in]' ) are the issue.
_getDemoRequest() {
String url = r"http://demoapi.ap-southeast-2.elasticbeanstalk.com/weatherobs?last=true&location[$in]=Bellambi&location[$in]=Nowra&location[$in]=Sydney Airport&location[$in]=Thredbo Top Station&location[$in]=Hobart&location[$in]=Coolangatta";
http.read(url).then(print);
}
In the URL I have tried prefixing the String with and without 'r' for a raw string to no avail.
I have also tried using httpClient with params with no success and the exact same error on the square brackets eg '[$in]'
String httpbaseUri = "http://xxxx.ap-southeast-2.elasticbeanstalk.com";
String qParams = r"?last=true&location[$in]=Bellambi&location[$in]=Nowra";
String path = "/weatherobs";
var _uri = new Uri.http(baseUri, path, qParams);
await httpClient.read(_uri, headers: {"Accept": "application/json"});
As a person with approximately 3 weeks of Flutter/Dart experience I believe its an elementary problem, but one in which several hours of research has uncovered no solution.
The ways the URI query parameters are structured (with the square brackets ie [$in]) are dictated by the feathers.js framework.
Any help would be appreciated.
It has been brought to my attention in another thread https://stackoverflow.com/questions/40568/are-square-brackets-permitted-in-urls :
That the URL Specification RFC 3986 generally does not permit square brackets in an URL.
My question was triggered as the get request works as intended in Postman, Chrome Browser and also javascript applications using axios.js, but not in an application developed in Flutter/Dart using standard http.read methods.
It doesn't look like [] are supported in the URL (except for the host IP for IPv6). See Are square brackets permitted in URLs?.
Please check if the API accepts them when they are encoded like:
void main() {
var url = r'http://demoapi.ap-southeast-2.elasticbeanstalk.com/weatherobs';
var locationKey = Uri.encodeQueryComponent(r'location[$in]');
var qParams = 'last=true&$locationKey=Bellambi&$locationKey=Nowra&$locationKey=Sydney Airport&$locationKey=Thredbo Top Station&$locationKey=Hobart&$locationKey=Coolangatta';
try {
print(Uri.parse(url).replace(query: qParams));
} catch(e) {
print(e);
}
}
DartPad example
See also api.dartlang.org/stable/1.24.3/dart-core/Uri/…
You can use this flutter package which allow you to communicate with your feathers js server from flutter app as said in: https://stackoverflow.com/a/65538226/12461921
I was using an HTTP POST method using the URL
"https://stream.twitter.com/1.1/statuses/filter.json" and in the body I was posting the key/value I wanted to get tweets from - for example "track=london". This was working fine.
Now I am trying to switch to AKKA-CAMEL and I am using their twitter consumer. I am using an endpoint URL of:
def endpointUri: String = s"twitter:////search?type=direct&keywords=${Settings.queryList()}&consumerKey=${tweeterCredentials.consumerKey}&consumerSecret=${tweeterCredentials.consumerSecret}&accessToken=${tweeterCredentials.accessToken}&accessTokenSecret=${tweeterCredentials.accessTokenSecret}"
I get a response from twitter but it is not in JSON and it is not the same information about the tweet as before. It just return the tweet text but before I was getting the whole metadata which I need to analyze.
Does somebody knows how to configure Camel URI to return JSON and the whole metadata as before?
Thanks
I got this to work by using the following syntax:
def endpointUri: String = s"twitter://streaming/filter?type=event&keywords=${Settings.queryList()}&consumerKey=${tweeterCredentials.consumerKey}&consumerSecret=${tweeterCredentials.consumerSecret}&accessToken=${tweeterCredentials.accessToken}&accessTokenSecret=${tweeterCredentials.accessTokenSecret}"
Where: Settings.queryList return a comma separated list of keyworkds. The object tweeterCredentials holds the keys from Tweeter to access the site.
Also it is necessary to set autoAck like this in Camel:
override def autoAck = true
This prevents a timeout exception.
I've had success creating objects with POST and Content-Type application/xml
I've also had success querying using Content-Type application/x-www-form-urlencoded with a blank request body which returns all of the object type depending on which URI I specify.
I can also get the same to work with something like PageNum=1&ResultsPerPage=1 in the request body and I have figured out how to incorporate that into the signature so I get a valid response.
However no matter how I format it, I cannot get anything other than a 401 response when I try to use a filter (something basic like Filter=FAMILYNAME :EQUALS: Doe). I've read over the OAuth Core 1.0 Revision A specifications on how all parameter names and values are escaped using the [RFC3986] percent-encoding. However I feel like I'm missing a step or formatting incorrectly. I've seen inconsistent information in my searching through Intuit's forums on what exactly is the proper format.
Any help on this would be greatly appreciated. I've been struggling with this for a good week now.
The response I get when trying to use a filter is:
HTTP Status 401 - message=Exception authenticating OAuth; errorCode=003200; statusCode=401
----Update----
I'm am seeing the same error when I try to use filters with the New IPP Developer Tools - IPP API Explorer. I'm using the IDS V2 QBO API Explorer. I'm able to use that tool to do a retrieve all Post and the response shows all of my customers, but when I try to use a filter I get :
Server Error
401 - Unauthorized: Access is denied due to invalid credentials.
You do not have permission to view this directory or page using the credentials that you supplied.
Any Ideas? If I'm getting the same error from the API Explorer tool, it makes me think the problem is something else entirely.
----Final Update----
I have finally had success with filters and I believe I have figure out what my problem was. I was always suspicious that I was able to get queries with pagination like "PageNum=1&ResultsPerPage=1" to work, but could not get something like "Filter=FAMILYNAME :EQUALS: Doe". I suspected there problem was with the white space in the filter format. What threw me off tracking this down earlier was that I could not get the filters to work in the IDS V2 QBO API Explorer. That made me suspect there was something else going on. I decided to ignore the API Explorer all together and focus on why I could get it to work the one way but no the other.
I believe my problem came down to improper encoding of the Filter's value in the signature. That explains the 401 invalid signature errors I was getting.
"Filter=Name :EQUALS: Doe" becomes "Filter=Name%20%3AEQUALS%20%3ADoe" after normalization.
Percent-Encoding that should give "Filter%3DName%2520%253AEQUALS%2520%253ADoe".
In essence you have to "double" encode the blank space and the colons, but not the equal sign. I tried many permutations of doing the encoding, but believe my mistake was that I was either not "double" encoding, or when I was double encoding I was including the '=' sign. Either way breaks your signature. Thanks for everyone's input.
I believe my problem came down to improper encoding of the Filter's value in the signature. That explains the 401 invalid signature errors I was getting.
I used an online tool to take me through the steps in properly signing an Oauth request. While going through those steps I realized my problem was with the steps where you normalize the request parameters and then percent-encode them. I was including the '=' of the filter in the normalization step, which breaks your signature. The tool I used can be found at:
http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/
Thanks for everyone's input.
Do you get a 401 with the same request in the API Explorer?
http://ippblog.intuit.com/blog/2013/01/new-ipp-developer-tool-api-explorer.html
Also, are you using the static base URL or retrieving it at runtime?
https://ipp.developer.intuit.com/0010_Intuit_Partner_Platform/0050_Data_Services/0400_QuickBooks_Online/0100_Calling_Data_Services/0010_Getting_the_Base_URL
If you are using the static base URL, try switching to the runtime base URL to see if you still get the error.
peterl answered one of my questions on here that may also answer yours. I had been trying to put the Filters in the body when they should have gone into the header. Here was peterl's code sample for getting all unpaid invoices (open balance greater than 0.00) for a particular customer.
http://pastebin.com/raw.php?i=7VUB6whp
public List<Intuit.Ipp.Data.Qbo.Invoice> GetQboUnpaidInvoices(DataServices dataServices, int startPage, int resultsPerPage, IdType CustomerId)
{
StringBuilder requestXML = new StringBuilder();
StringBuilder responseXML = new StringBuilder();
var requestBody = String.Format("PageNum={0}&ResultsPerPage={1}&Filter=OpenBalance :GreaterThan: 0.00 :AND: CustomerId :EQUALS: {2}", startPage, resultsPerPage, CustomerId.Value);
HttpWebRequest httpWebRequest = WebRequest.Create(dataServices.ServiceContext.BaseUrl + "invoices/v2/" + dataServices.ServiceContext.RealmId) as HttpWebRequest;
httpWebRequest.Method = "POST";
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Headers.Add("Authorization", GetDevDefinedOAuthHeader(httpWebRequest, requestBody));
requestXML.Append(requestBody);
UTF8Encoding encoding = new UTF8Encoding();
byte[] content = encoding.GetBytes(requestXML.ToString());
using (var stream = httpWebRequest.GetRequestStream())
{
stream.Write(content, 0, content.Length);
}
HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
using (Stream data = httpWebResponse.GetResponseStream())
{
Intuit.Ipp.Data.Qbo.SearchResults searchResults = (Intuit.Ipp.Data.Qbo.SearchResults)dataServices.ServiceContext.Serializer.Deserialize<Intuit.Ipp.Data.Qbo.SearchResults>(new StreamReader(data).ReadToEnd());
return ((Intuit.Ipp.Data.Qbo.Invoices)searchResults.CdmCollections).Invoice.ToList();
}
}
protected string GetDevDefinedOAuthHeader(HttpWebRequest webRequest, string requestBody)
{
OAuthConsumerContext consumerContext = new OAuthConsumerContext
{
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
SignatureMethod = SignatureMethod.HmacSha1,
UseHeaderForOAuthParameters = true
};
consumerContext.UseHeaderForOAuthParameters = true;
//URIs not used - we already have Oauth tokens
OAuthSession oSession = new OAuthSession(consumerContext, "https://www.example.com",
"https://www.example.com",
"https://www.example.com");
oSession.AccessToken = new TokenBase
{
Token = accessToken,
ConsumerKey = consumerKey,
TokenSecret = accessTokenSecret
};
IConsumerRequest consumerRequest = oSession.Request();
consumerRequest = ConsumerRequestExtensions.ForMethod(consumerRequest, webRequest.Method);
consumerRequest = ConsumerRequestExtensions.ForUri(consumerRequest, webRequest.RequestUri);
if (webRequest.Headers.Count > 0)
{
ConsumerRequestExtensions.AlterContext(consumerRequest, context => context.Headers = webRequest.Headers);
if (webRequest.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded")
{
Dictionary<string, string> formParameters = new Dictionary<string, string>();
foreach (string formParameter in requestBody.Split('&'))
{
formParameters.Add(formParameter.Split('=')[0], formParameter.Split('=')[1]);
}
consumerRequest = consumerRequest.WithFormParameters(formParameters);
}
}
consumerRequest = consumerRequest.SignWithToken();
return consumerRequest.Context.GenerateOAuthParametersForHeader();
}
You can also see my original Question Here on StackOverflow: Query for All Invoices With Open Balances using QuickBooks Online (QBO) Intuit Partner Platform (IPP) DevKit.
Below I have this code:
string _strTemplate = _strDownloadTemplate + IDReq + "/" + _strFileName;
Uri url = new Uri(_strTemplate);
As you can see, I'm converting the strTemplate (which carries the link of a page that I need to sent by email for the user) to a URL Format. My email body has several fields that I'm replacing with the correct value:
strMailMessage = strMailMessage.Replace("_LinkTemplate", url);
I'm getting an error because the method string.Replace takes strings as parameters only.
Is there a way to get around this?
I was thinking about pass the URL value through my page (page.aspx) but if there's a way to do so through this method, it would be better for me.
Thanks!
Assuming this is C# and .NET, yes, String.Replace() works with strings.
Did you try:
strMailMessage = strMailMessage.Replace("_LinkTemplate", url.ToString());