Dart: client.post() method in the http.dart package is hanging and isn't returning a future - http

I am currently implementing a simple ONVIF client program in dart and am having a bit of trouble with handling futures in the http.dart package.
In my code below, the client.post() method returns a Future<response> (containing body/header/status code and so on...) and in my case I would need to receive this before the if/else statement, hence why I have used await. Trouble is the program just hangs and doesn't proceed past the client.post()line.
I know I might need to do a client.close() somewhere but I've tried lots of different ways and nothing works. Here is my current code with some comments to try and explain it a bit:
// The Variables:
// reqSysDateAndTime is a soap message we are sending to the device.
// onvifDev is just a place where the device details are stored.
// probeMatch is a class that stores important info from the ws-discovery stage.
Future<String> checkXaddrsAndGetTime(Device onvifDev, ProbeMatch probeMatch) async {
// Set up the client and uri.
Uri uri = Uri.parse(probeMatch.xaddrs);
http.Client client = http.Client();
// Send the POST request, with full SOAP envelope as the request body
print('[Setup]: Listening for Date/Time response...');
Response response = await client.post(uri, body: reqSysDateAndTime);
print("${response.body}");
// Determine if the address is usable or not.
if (response != null) {
// Set this address as 'working'
onvifDev.xAddrs = probeMatch.xaddrs;
return response.body;
}
else {
return null; // The address does not work
}
}
I also know that this isn't an issue with the actual body of the request because if I do...
client.post(uri, body: reqSysDateAndTime).then((onValue) => print(onValue.body));
...instead, it will print out the response which I'm expecting.
I understand that this is probably a small fix that I'm missing but any help would be much appreciated.
Cheers.

To briefly answer my own question, turns out it was a silly error on my part where the address I was using was link-local - hence why it was hanging at client.post(). There is a nice method in the InternetAddress class here, amongst some other useful methods, which checks to see if the address is link-local or not.

Related

PutAsJsonAync not setting body properly

I'm calling a 3rd party API right now. In post man I can do this, and it does what I'm expecting. Sets these two fields to "cancelled".
Here are the current headers in postman:
The verb that postman is using is PUT. POST is currently not supported by the API.
In code I'm doing this:
var idcInput = new IdcInputParameters(
status: "cancelled",
ivrstatus: "cancelled"
);
// pass the updated parameters to RFS
using HttpResponseMessage httpResponse = await RfsApiClient.ApiClient.PutAsJsonAsync(uri, idcInput);
var res = await httpResponse.Content.ReadAsStringAsync();
if (httpResponse.IsSuccessStatusCode)
{
Idc? idc = JsonConvert.DeserializeObject<Idc>(res);
return idc;
}
The JsonSerializer outputs the following:
"{\"status\":\"cancelled\",\"ivrstatus\":\"cancelled\"}"
I'm not 100% sure as to whats going on, but when I pass idcInput I'm getting back a 500 error from the API saying one or more input parameters is invalid. This leads me to believe that the body of the request is empty.
I've tried a lot of the answers on stack overflow however nothing's worked yet.
How do I set the body of the request to be a RAW Json object like postman?

Is it a bad idea for a WebAPI to return different types depending on some condition?

I have this code which returns a different type depdending on a condition:
[HttpGet("{questionId:int}/specific")]
public async Task<IActionResult> GetSpecificByQuestionIdAsync(int questionId)
{
IActionResult result = Ok();
try
{
var question = await _questionService.GetQuestionByIdAsync(questionId);
if (question != null)
{
if (question.TypeName == "TypeA")
{
var typeA = await _questionService.GetTypeAQuestionAsync(questionId);
result = Ok(typeA);
}
else if (question.TypeName == "TypeB")
{
var typeB = await _questionService.GetTypeBQuestionAsync(questionId);
result = Ok(typeB);
}
}
}
catch (Exception ex)
{
result = StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
return result;
}
I want to ask if this is a bad idea or is this gonna cause some problem. I tried searching but couldn't get any info regarding returning different types for REST APIs. Or is it better to make this just 2 separate calls?
I prefer to do not return diferent types from the same URL. This will make more dificult to consume your API, as you can return diferent types of response body for the same response code (200). But if TypeA and TypeB inhiret from the same base Type and you think that would not be a problem to your consumers to handle the differences from TypeA and TypeB, maybe this should not be a problem.
I have worked with some payment APIs that have a property that has a diferent types based on the payment method used by the user. In this case, was easy to handle because the root object was always the same, just one property could be completely different, and I could known wich kind of cast I nedded to do based on the paymentType property at the root object.
If you look at some API guide lines, you probably will not see anyone saying that is a bad practice. But you will always see that are a pattern at the same url always return the same type of response.
Sametimes this kind of differente results are acceptable, and sometimes are not. To answer your question you should think:
How hard will be to the consumer of this API to handle this diferent objects?
Some good api good practice references are:
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
https://oai.github.io/Documentation/start-here.html
One problem that I saw in your code is that you are writting the exception message on your 500 error result. This is a great risk as sometimes the exception message could show potential security risks of your application.

INDY Error 212 while executing createPairwise

I am trying to execute createPairwise function of indy-sdk package.
It throws an INDY Error 212 => WalletItemNotFound.
(I also executed createAndStoreMyDid function)
Here is my code
let [myDid, myVerkey] = await sdk.createAndStoreMyDid(await indy.wallet.get(), {});
let theirVerkey = await sdk.keyForDid(await indy.pool.get(), await indy.wallet.get(), theirDid);
let meta = JSON.stringify({
theirEndpointDid: theirEndpointDid,
verified: false // Indicates that the owner of the agent has confirmed they want to stay connected with this person.
});
//FIXME: Check to see if pairwise exists
await sdk.createPairwise(await indy.wallet.get(), theirDid, myDid, meta);
Can anybody help me out?
In your code, there's actually 2 places where IndySDK 1.11.1 can throw 212 WalletItemNotFound.
1. The first is on this line:
let theirVerkey = await sdk.keyForDid(await indy.pool.get(), await indy.wallet.get(), theirDid);
IndySDK will first try to see if the DID under variable theirDid is not one of your DIDs, stored in your wallet. If no, it will try to find this DID on the ledger. If it's still not found, it will throw WalletItemNotFound. You can check out this behaviour in the IndySDK Rust code here:
https://github.com/hyperledger/indy-sdk/blob/v1.11.1/libindy/src/commands/did.rs#L331
2. However I assume this is not actually your case and you having this error coming out from
wait sdk.createPairwise(await indy.wallet.get(), theirDid, myDid, meta);
If you look how is this method implemented in Rust
https://github.com/hyperledger/indy-sdk/blob/v1.11.1/libindy/src/commands/pairwise.rs#L84
you will see that it's calling get_indy_record for both theirDid and myDid you've supplied. Hence you can't create pairwise record without having both DIDs stored in wallet first. You can assure your wallet contains theirDid by calling storeTheirDid method. In
sdk.storeTheirDid(wh, {did:'V4SGRU86Z58d6TV7PBUe6f', verkey: 'GJ1SzoWzavQYfNL9XkaJdrQejfztN4XqdsiV4ct3LXKL'} )
After calling this, you will be able to call createPairwise between you and them without an issue.
IndySDK version / caching note
I think you might be using some older version of IndySDK. In IndySDK 1.11.1 when keyForDid resolves something from ledger, it actually caches this data, so the code you've posted actually worked out of the box for me without an error.

Get HTTP Status using swift

I am sorry, I haven't found an answer for my question(( Please, don't be very harsh, I am not a professional programmer, but I keep learning and hope once I will be able to answer someone's question))
I am trying to get HTTP Status of the link (I am sort of generating links depending on one database entries code, like ABCDEF, I keep them in an array and then generate a link to second database, like www.blablabla.ABCDEF.net), so I can see whether the page exists in database or not.
I have written this code, but something is wrong. So maybe it's a question like: "What's wrong with my code?" But on the stack they also say, you have to show your attempts of problem solving...
I wish I could keep it all swift, without any additional modules or something, I think NSHTTPURLResponse must be enough, but I am using it somehow wrong.
Looking forward to help and replies))
var err: NSError!
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData!, error: err) -> Void in
if (err != nil) {
let httpStatus: NSHTTPURLResponse = response as NSHHTPURLResponse
for myLink in allLinks {
println("HERE IS THE CURRENT STATUS CODE" + httpStatus.statusCode + "OF A LINK:" + myLink)
if httpStatus.statusCode == 200 {println("SUCCESS!!!!!")}
}
}
}
The fundamental issue here is that you appear to be looking at the statusCode only if the err is not nil. But if you have error, you probably don't have status code. The error parameter to the closure indicates fundamental network issue that probably prevented you from getting to the page in question. The statusCode is generally only meaningful if the error was nil (i.e. you succeeded in connecting to the server), in which case the statusCode is how the server informs you of its ability to service the HTTP request.
A couple of minor things:
You don't need the var err: NSError! line (because the error object is passed as parameter to the closure). This variable you've declared is not used here.
I don't see how error: err as the third parameter to the closure could have worked. The syntax is "variableName: variableType", but there is no type of err.
Likewise, your code is referring to a non-existent class, NSHHTPURLResponse. In Swift 3 and later, it's HTTPURLResponse.
It's probably prudent to do if let for the retrieval of the HTTPURLResponse in order to get the statusCode.
I'm unclear as to your intent in iterating through allLinks, because this connection is just for a given request, not a bunch of links. Just look at the statusCode in light of the particular request. If you need to test multiple URLs, then you do a separate request for each.
We should consider any codes between 200 and 299 as success, not just 200. I'd suggest using the range 200 ..< 300.
Thus:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, let httpResponse = response as? HTTPURLResponse, error == nil else {
print("No valid response")
return
}
guard 200 ..< 300 ~= httpResponse.statusCode else {
print("Status code was \(httpResponse.statusCode), but expected 2xx")
return
}
// everything OK, process `data` here
}
task.resume()
I also made a few other changes (updated for Swift 3 and later; use URLSession rather than URLConnection; I think error is fine choice for the variable name; I prefer the trailing closure syntax; I tend to use inferred types for closure parameters to make the declaration a little more concise, etc.), but all of that is immaterial to the question at hand: Hopefully this illustrates how one checks the status code.
For Swift 2 rendition, see previous revision of this answer.
[Swift 5.2]
Hi there, you can try this one:
let task = URLSession.shared.dataTask(with: yourRequest) {
(data, response, error) in
guard let response = response else {
print("Cannot found the response")
return
}
let myResponse = response as! HTTPURLResponse
print("Status Code:", myResponse.statusCode)
}
task.resume()
Here is the output
Status Code: 200
(This is your StatusCode)

Implementing exception handling in a function which returns a Stream

I'm implementing a function which returns a Stream. I'm not sure how to implement the error handling, what is best practice?
For functions which return a Future, it's best practice never to throw a synchronous error. Is this also true for functions which return a Stream?
Here's an example of what I'm thinking:
Stream<int> count() {
var controller = new StreamController<int>();
int i = 0;
try {
doSomethingThatMightThrow();
new Timer.repeating(new Duration(seconds: 1), () => controller.add(i++));
} on Exception catch (e) {
controller.addError(e);
controller.close();
}
return controller.stream;
}
In general it is true for Streams as well. The main idea is, that users should only need to handle errors in one way. Your example moves all errors to the stream.
There are circumstances where immediate errors are better (for instance you could make the error is due to a programming error and should never be handled anyways, or if you want to guarantee that a Stream never produces errors), but sending the error through a stream is almost always a good thing.
Small nit: a Stream should usually (there are exceptions) not produce any data until somebody has started listening. In your example you are starting a Timer even though you don't even know if there will ever be a listener. I'm guessing the example is reduced and not representative of your real code, but it is something to look out for. The solution would be to use the StreamController's callbacks for pause and subscription changes.
I've updated the example to take on-board Florian's comments.
In my real use case, I don't ever want to buffer the results, so I'm throwing an UnsupportedError if the stream is paused.
I've made it a terminating stream, rather than an infinite one.
If the user of this function adds a listener asynchronously after a few seconds, then they will lose the first couple of results. They shouldn't do this. I guess that's something to document clearly. Though perhaps, I could also throw an error if the subscribe state changes after the first data has been received, but before a close has been received.
Stream<int> count(int max) {
var controller = new StreamController<int>(
onPauseStateChange: () => throw new UnsupportedError('count() Stream pausing not supported.'));
int i = 0;
try {
doSomethingThatMightThrow();
new Timer.repeating(new Duration(seconds: 1), () {
if (!controller.hasSubscribers)
return;
controller.add(i++);
if (i >= max)
controller.close();
});
} on Exception catch (e) {
controller.addError(e);
controller.close();
}
return controller.stream;
}

Resources