I'm trying to make an app that makes an API call and get a JSON array in response, and then, after some parsing it returns a string from the API.
Here is the code for the request (in kotlin):
private fun apiCall(context: Context): String {
val url = "https://example.com"
val queue = Volley.newRequestQueue(context)
var day = ""
val jsonArrayRequest = JsonArrayRequest(
Request.Method.GET, url, null,
{ response ->
val listData = ArrayList<String>()
if (response != null) {
for (i in 0 until response.length()) {
listData.add(response.getString(i))
}
}
if (listData.size != 0){
val jsonObject = JSONTokener(listData[0]).nextValue() as JSONObject
day = jsonObject.getString("day")
} else{
context.getString(R.string.if_there_is_no_special_day)
}
Log.d("notification", "it worked: $response, $day")
},
{response ->
Log.d("notification", "error on request: $response")
})
queue.add(jsonArrayRequest)
return day
}
and here is where I call the function:
private fun makeWebRequest(message: String){
val day = apiCall(this)
Log.d("notification", "request complete: $day")
But when I check the logs I see that the log that is called after the function is called shows first and only then the response is printed to the log.
20:23.451 D/notification: request complete: [nothing]
20:23.451 D/notification: it worked: [response]
So my question is this: is there any way to wait for the response to come before I return the response?
Related
I'm trying to execute a simple HTTP request:
#Grab(group='io.github.http-builder-ng', module='http-builder-ng-apache', version='1.0.4')
import groovyx.net.http.*
HttpBuilder http = HttpBuilder.configure {
request.uri = 'https://stackoverflow.com'
request.accept = ['text/html']
}
http.get {
response.success { FromServer fromServer ->
println("Got status $fromServer.statusCode $fromServer.message")
println("Has body: $fromServer.hasBody")
try {
List<String> bodyLines = fromServer.reader.withReader { it.readLines() }
String body = bodyLines.join("\n")
if (body.empty) {
println("Body is empty.")
} else {
println("Body: $body")
}
} catch (Exception e) {
println("Reading successful response failed. $e")
}
}
}
The output is:
Got status 200 OK
Has body: true
Body is empty.
What's the secret to reading the response body? Groovy 2.5.19.
The response.success handler takes a BiFunction<FromServer,Object> as well where the second parameter is the body content object. If you add a second parameter to your success closure it should have the body content in it, e.g.
response.success { FromServer fromServer, Object body ->
// your stuff
}
void getData() async {
http.Response response = await http.get(Uri.parse(
'https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$apiKey'));
if (response.statusCode == 200) {
String data = response.body;
var decodedData = jsonDecode(data);
String city = jsonDecode(decodedData)['name'];
double temperature = jsonDecode(decodedData)['main']['temp'];
int condition = jsonDecode(decodedData)['weather'][0]['id'];
print(city);
print(temperature);
print(condition);
} else {
print(response.statusCode);
}
}
Solved. I was using code from a tutorial that was about a year old. I followed the flutter docs to solve this problem by creating a WeatherData class with the city, temperature and condition properties and called
the http request through the WeatherData class. It worked without errors
I am exploring Microsoft Computer Vision's Read API (asyncBatchAnalyze) for extracting text from images. I found some sample code on Microsoft site to extract text from images asynchronously.It works in following way:
1) Submit image to asyncBatchAnalyze API.
2) This API accepts the request and returns a URI.
3) We need to poll this URI to get the extracted data.
Is there any way in which we can trigger some notification (like publishing an notification in AWS SQS or similar service) when asyncBatchAnalyze is done with image analysis?
public class MicrosoftOCRAsyncReadText {
private static final String SUBSCRIPTION_KEY = “key”;
private static final String ENDPOINT = "https://computervision.cognitiveservices.azure.com";
private static final String URI_BASE = ENDPOINT + "/vision/v2.1/read/core/asyncBatchAnalyze";
public static void main(String[] args) {
CloseableHttpClient httpTextClient = HttpClientBuilder.create().build();
CloseableHttpClient httpResultClient = HttpClientBuilder.create().build();;
try {
URIBuilder builder = new URIBuilder(URI_BASE);
URI uri = builder.build();
HttpPost request = new HttpPost(uri);
request.setHeader("Content-Type", "application/octet-stream");
request.setHeader("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY);
String image = "/Users/xxxxx/Documents/img1.jpg";
File file = new File(image);
FileEntity reqEntity = new FileEntity(file);
request.setEntity(reqEntity);
HttpResponse response = httpTextClient.execute(request);
if (response.getStatusLine().getStatusCode() != 202) {
HttpEntity entity = response.getEntity();
String jsonString = EntityUtils.toString(entity);
JSONObject json = new JSONObject(jsonString);
System.out.println("Error:\n");
System.out.println(json.toString(2));
return;
}
String operationLocation = null;
Header[] responseHeaders = response.getAllHeaders();
for (Header header : responseHeaders) {
if (header.getName().equals("Operation-Location")) {
operationLocation = header.getValue();
break;
}
}
if (operationLocation == null) {
System.out.println("\nError retrieving Operation-Location.\nExiting.");
System.exit(1);
}
/* Wait for asyncBatchAnalyze to complete. In place of this wait, can we trigger any notification from Computer Vision when the extract text operation is complete?
*/
Thread.sleep(5000);
// Call the second REST API method and get the response.
HttpGet resultRequest = new HttpGet(operationLocation);
resultRequest.setHeader("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY);
HttpResponse resultResponse = httpResultClient.execute(resultRequest);
HttpEntity responseEntity = resultResponse.getEntity();
if (responseEntity != null) {
String jsonString = EntityUtils.toString(responseEntity);
JSONObject json = new JSONObject(jsonString);
System.out.println(json.toString(2));
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
There is no notification / webhook mechanism on those asynchronous operations.
The only thing that I can see right know is to change the implementation you mentioned by using a while condition which is checking regularly if the result is there or not (and a mechanism to cancel waiting - based on maximum waiting time or number of retries).
See sample in Microsoft docs here, especially this part:
// If the first REST API method completes successfully, the second
// REST API method retrieves the text written in the image.
//
// Note: The response may not be immediately available. Text
// recognition is an asynchronous operation that can take a variable
// amount of time depending on the length of the text.
// You may need to wait or retry this operation.
//
// This example checks once per second for ten seconds.
string contentString;
int i = 0;
do
{
System.Threading.Thread.Sleep(1000);
response = await client.GetAsync(operationLocation);
contentString = await response.Content.ReadAsStringAsync();
++i;
}
while (i < 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1);
if (i == 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1)
{
Console.WriteLine("\nTimeout error.\n");
return;
}
// Display the JSON response.
Console.WriteLine("\nResponse:\n\n{0}\n",
JToken.Parse(contentString).ToString());
I am very new to Swift.
I want to create something like API on Swift for my educational app.
I have this code:
static func getFilm(filmID: Int) -> String {
print("getFilm")
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
var request = URLRequest(url: url)
var returnData: String = ""
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if var responseVar = response, var dataVar = data {
print(responseVar)
returnData = String(data: dataVar, encoding: .utf8)
} else {
print(error)
}
}
task.resume()
return returnData
}
And I try to convert Data to String in this line: returnData = String(data: dataVar, encoding: .utf8)
Swift compiler gives me an error, and change this line to
returnData = String(data: dataVar, encoding: .utf8)!
, when I execute this line I get empty returnData variable.
If I use basic example line
print(String(data: data, encoding: .utf8))
everything will be OK and I can see data in XCode console.
So, how I can convert Data to String?
This is an example using a completion handler:
class func getFilm(filmID: Int, completion: #escaping (String) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
print(error!)
completion("")
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(returnData)
} else {
completion("")
}
}
}.resume()
}
And you call it
MyClass.getFilm(filmID:12345) { result in
print(result)
}
In case of an error the completion handler returns an empty string.
MyClass is the enclosing class of getFilm method. Most likely the web service will return JSON, so you might need to deserialize the JSON to an array or dictionary.
In a more sophisticated version create an enum with two cases and associated values
enum ConnectionResult {
case success(String), failure(Error)
}
With a little more effort demonstrating the subtle power of Swift you can return either the converted string on success of the error on failure in a single object.
class func getFilm(filmID: Int, completion: #escaping (ConnectionResult) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
completion(.failure(error!))
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(.success(returnData))
} else {
completion(.failure(NSError(domain: "myDomain", code: 9999, userInfo: [NSLocalizedDescriptionKey : "The data is not converible to 'String'"])))
}
}
}.resume()
}
On the caller side a switch statement separates the cases.
MyClass.getFilm(filmID:12345) { result in
switch result {
case .success(let string) : print(string)
case .failure(let error) : print(error)
}
}
I had this problem, you can't use encoding: .utf8 for unpredictable data. It will return nil every time.
Use this instead:
String(decoding: data, as: UTF8.self)
For anyone coming in future (which are probably not interested in OP's film code?!);
Simply, try something like:
extension Data {
public func toString() -> String {
return String(data: self, encoding: .utf8) ?? "";
}
}
See also my toHex related answer
I have a case where I need to call several different web endpoints and need to do the same setup and tear down for every call. I am trying to write a more generic method where I can pass in the method I want to execute along with the package to send to the endpoint and expect a string return.
From my code I can make this call:
var ret = WebServiceHandler.Execute(WebServiceHandler.LoadNewAsset(package));
The definition of Execute looks like:
internal static string Execute<T>(Func<T, string> executeThisAction)
{
Func<T, string> resp;
Setup();
resp = executeThisAction;
CleanUp();
return resp.ToString();
}
This is one of the methods I want to execute:
internal static Func<CarsWS_AssetLoad, string> LoadNewAsset(AssetLoad package)
{
string resp;
try
{
// Make the web service call...
var assetLoadReturn = _service.LoadNewAsset(new LoadNewAssetRequest {UserCredentialsHeader = _credentials, asset = package});
// Evaluate results...
if (assetLoadReturn.LoadNewAssetResult.responseType == "Success")
resp = (result != null && !String.IsNullOrEmpty(result.asset.assetID))
? "Got assetID: " + result.asset.assetID
: "No assetID returned.";
else
resp = result.responseDescription.Trim();
}
catch (Exception ex)
{
resp = "Error calling LoadNewAsset()." + Environment.NewLine + ex.GetFullMessage();
}
return resp; // <== THIS IS NOT A VALID RETURN <== //
}
My brain is shutting off at this point. How do I return the string back up the call stack correctly???
I assume that in your LoadNewAsset method the CarsWS_AssetLoad class is actually the same as AssetLoad and it was just a editing issue with your question.
That being the case, I think this is what you want:
internal static string Execute<T>(Func<T, string> executeThisAction, AssetLoad package)
{
string resp;
Setup();
resp = executeThisAction(package);
CleanUp();
return resp;
}
internal static Func<AssetLoad, string> LoadNewAsset()
{
return package =>
{
string resp;
var assetLoadReturn = _service.LoadNewAsset(new LoadNewAssetRequest {UserCredentialsHeader = _credentials, asset = package});
if (assetLoadReturn.LoadNewAssetResult.responseType == "Success")
resp = (result != null && !String.IsNullOrEmpty(result.asset.assetID))
? "Got assetID: " + result.asset.assetID
: "No assetID returned.";
else
resp = result.responseDescription.Trim();
return resp;
};
}
The use of the variable result in the LoadNewAsset is a little confusing too. Did you mean to use LoadNewAsset instead?
The above code should be able to workable for you, but it's really not the right way to go about coding this.
I assume that the Setup & CleanUp code is all about instantiating the _service that you're calling?
So the key is to code it this way:
internal static string Execute<T>(Func<IAssetService, T, string> serviceCall, AssetLoad package)
{
string resp;
var service = Setup();
resp = serviceCall(service, package);
CleanUp(service);
return resp;
}
internal static Func<IAssetService, AssetLoad, string> GetLoadNewAssetFunc()
{
return (service, package) =>
{
string resp;
var assetLoadReturn = service.LoadNewAsset(new LoadNewAssetRequest {UserCredentialsHeader = _credentials, asset = package});
if (assetLoadReturn.LoadNewAssetResult.responseType == "Success")
resp = (result != null && !String.IsNullOrEmpty(result.asset.assetID))
? "Got assetID: " + result.asset.assetID
: "No assetID returned.";
else
resp = result.responseDescription.Trim();
return resp;
};
}
Ideally if you would bring the Setup & CleanUp code into the Execute method so that the only way to call the set-up and clean-up code is thru the Execute method.
Even better, if the service class implements IDisposable then your execute code would look like this:
internal static string Execute<T>(Func<IAssetService, T, string> serviceCall, AssetLoad package)
{
using (var service = Setup())
{
return serviceCall(service, package);
}
}
Let me know if I've missed anything.
Replace:
internal static string Execute<T>(Func<T, string> executeThisAction)
with
internal static string Execute<T>(Func<T, string> executeThisAction, T argument)
then replace:
internal static Func<CarsWS_AssetLoad, string> LoadNewAsset(AssetLoad package)
with
internal static string LoadNewAsset(AssetLoad package)
then to call it:
var ret = WebServiceHandler.Execute(WebServiceHandler.LoadNewAsset, package);