Http get Request always must fired twice to work? Help pls


#1

Hello guys.
Im facing the issue that my http request only works the second time when i click log in.
This is what gets fired when i click login:

var url = 'http:/blblblbsomewebsite.com/User_Login.php?email=' + credentials.email.toString() + '&password=' + credentials.password.toString();
this.http.get(url).map(res => res.text()).subscribe(data => this.accessgranted = data, () => console.log('Secret Quote: ' + this.accessgranted ));
let access = (this.accessgranted === 'access');
observer.next(access);
observer.complete();

Its a normal Http request to see if the user have the access to login.
The first time it gets fired i always gets a time out i think cause the log doesnt output something at all.
At the second time i click it right after the first click it works and i log in.
What kind of weird error is this? Its annoying that on every http request the user must fire things twice.

Issue is present on browser and also on ionic view android/ios.


#2

Sloved it.
The problem here is that the http request was async.
So this line:
let access = (this.accessgranted === 'access');
got always fired before this line got the response whitch was needed:
this.http.get(url).map(res => res.text()).subscribe(data => this.accessgranted = data, () => console.log('Secret Quote: ' + this.accessgranted ));

So all i had to do is to insert a timeout for 500 milliseconds. Why 500? Because in Wireshark i found out that my http request needed 200 milliseconds to get the string from the http request.
So the programm can do now get the response async and waits 500 milliseconds to be sure that i already got the string i need to continue. So it looks now like this:

var url = 'http://someurl.com/User_Login.php?email=' + credentials.email.toString() + '&password=' + credentials.password.toString();
this.http.get(url).map(res => res.text()).subscribe(data => this.accessgranted = data);
setTimeout(() => {
  let access = (this.accessgranted === 'access');
  observer.next(access);
  observer.complete();
},500);

#3

For anybody else who comes across this topic, the proposed “solution” is terrible.

Instead, you should only rely on the response being present inside the subscribe block.

Additionally, the backend API here is braindead. Credentials should never be in GET requests, always POST.


#4

Although I agree with you conclusion… the commentary is not very productive for the community. Calling a solution “braindead” is kinda making a statement about the individual who wrote the code.

this is clearly just my opinion and you can ignore it if you like


#5

I’m sorry you feel that way, but I feel that it’s important to be able to criticize code harshly without it being taken as a personal attack on the person who wrote it. Maybe it came from a lack of knowledge, maybe a momentary instance of laziness. I’ve been guilty of making both of those sorts of mistakes many times over, and know I’ll do both again. When I do so publicly, and more importantly promote the results of said mistake to others, I would hope that the community would say so.


#6

If you read the code of conduct, it is clear that although you intentions are good, this type of response probably doesn’t meet that standard…


#7

Perhaps I should go elsewhere then.


#8

@rapropos You are right i dont take it as a personal attack. Why should i ? Because when im posting a Solution and someone do a better whon or say me whats wrong with the code i will be get better faster then if everybody would say its perfect. So dont worry :smiley:

Also im a very new beginner with angular 2 and the ionic framework so this solution was only for now until i know a better one. I only wanted to get it to work no matter how terrible the solution is. I think most of the programmers understanding this matter.

@aaronksaunders @rapropos
can you two guys give me a better solution how i can handle this? So i can correct it and post it here as the better solution? Would be great.


#9

It is difficult to say without knowing more about how the rest of your code is structured, but in general,

this.http.post('url', credentials).subscribe((rsp) => {
  // you can call rsp.text() or more typically rsp.json() here
  // but most importantly inside here is the only place you
  // can rely on having the response
});

If you are attempting to separate the backend callback from the client, which is a very typical situation, then it is best to return an Observable from your backend communication service. Often this is done with a map transform, like so:

authenticate(email: string, pwd: string): Observable<AuthToken> {
  return this.http.post('url', {email: email, pwd: pwd}).map((rsp) => {
    return rsp.json();
  });
};

Personally I use the heuristic of “instinctually have ‘return’ be the first thing you type in a function like this” (that is returning a future). There are some rare occasions where that’s not the case, but in the vast majority of cases it saves you from silly bugs. In particular, this very common idiom (which I have also been very harshly critical of here and drawn ire from mods for doing so).

So in this case, you would subscribe to the return value from authenticate, and inside that subscribe block would be the only place where you can rely on the value being accurate.

In a common situation, you would be assigning that value to a property in a component controller, which would be referenced by a template, often in an ngFor loop. It is imperative that you either protect in the template against that variable having not been initialized yet (which I think is a suboptimal but defensible alternative) or to initialize the property to a functioning dummy (my preferred solution), such as an empty array or blank string. That way your app will function smoothly without needing to resort to brittle timeouts.


#10

As for my comment about the backend use of GET versus POST, here is some background as to why I think it is such a bad idea.

I hope that I do not throw the term “braindead” around lightly, but in my opinion this is the sort of thing that gets people sued for not taking commercially reasonable efforts to protect private information of their users.


#11

As for my comment about the backend use of GET versus POST, here is some background as to why I think it is such a bad idea.

Thanks for that. This was simple to read and understand. Well GET and POST does give the same Result thats true. But the difference here is that POST does send sensible user information through the body and GET send it through the browser. The bad thing here is that with GET Method the information is unsafe. Because it could easy restored from the Browser history in case someone gain Access to the System and a lot more security issues. Did i understand it right? If so then i ask myselfe when someone would ever need a GET request? Because with this information i will alwayse use POST instead?


#12

Well let’s say that your user has successfully authenticated, and then wish to e.g. load a news feed, their personal feed (Facebook/Twitter like) or just their personal messages, then they’d use the GET request, because they don’t (necessarily) need to send any parameters as a post or in the URL. An example could be

GET http://api.mydomain.com/v1/news/

Then their authorization details are either sent via cookies or the HTTP Authorization header. (That shouldn’t be their login details though, but some sort of session information the server can verify and find the user connected to) Edit: They would receive this information after performing the login. They log in, the server returns this value to them and the client stores it for use in future requests.

Edit: Also, I’d recommend changing the accepted answer (I think that’s possible, at least), so that it wont bring someone onto the wrong path in the future. :slight_smile:


#13

@mich356c
Ah ok now i understand this cases of GET and POST.

but some sort of session information the server can verify

Ah ok so how about a generated access token. With that token the user would get the personal information like news feed or messages and the login details wouldnt be send through GET in the browser? That would be safe?

Also, I’d recommend changing the accepted answer (I think that’s possible, at least), so that it wont bring someone onto the wrong path in the future. :slight_smile:

Yeah i will do that when i changed my code to the right and safe one. I dont have the time at the moment to programm it but when i do ill post it here and the mark it as the solution.


#14

Quick note about the example I provided: It could also be a POST request and work just fine, but when you’re not sending any data to the server there’s no point.

Generally yeah, here’s a step by step example:

  1. The user authenticates with the server (A POST requesting with the username/email and password)
  2. If the authentication is successful, the server returns e.g. a JSONWebToken (Read more about JWT here) generated and signed by the server itself. The “payload” of this JWT will then be something to uniquely identify the user who signed in, e.g. email or a user ID. Look at the example given on the previous link, the data in the payload can be decoded by anyone who gets the token, so it should not be sensitive data.
  3. The client the stores the JWT for later use

When performing requests later on (GET, POST, or any other method), the client would include the JWT in either way described in the previous post. Then the server receives the request, and

  1. Checks if the JWT is set
  2. VERIFIES the JWT to make sure it was the server itself that signed it
  3. If verification of the JWT is successful, the server now has the user ID from the JWT so it knows what data to look up and returns it to the user

You don’t have to use JWT though, you could just generate your own tokens and link them to a user in your database, if you find that easier. Then you’d just have to look up the token received from the user in the database, and then you know which user you’re communicating with.

Edit: You could simply make the server set the token in an HTTP only cookie, then the client doesn’t have to think much about how to store it, but must use withCredentials: true on the requests it is sending to include the cookie in the request.

let options = new RequestOptions({ withCredentials: true });

this.http.post(url body, options)...
this.http.get(url, options)...

Then the server just checks for the cookie, and again either verifies the JWT (if JWT), or looks up the token in the DB, and blah blah blah… :stuck_out_tongue:

Edit 2: I like edits.


#15

excellent answer! Thank you so much! Things got clear as hell for now!


#16

@mich356c
Well i tried to test my new code now on a real android device with a real apk. But the http request doesnt work anymore. So i debugged it in firefox and got this output of the console whitch i think is the problem that causes my login issue on devices:

Request denied: … CORS-Header ‘Access-Control-Allow-Origin’ missing

How can i resolove this issue?

EDIT: Ok i can login now in Browser by adding this to my api.php file:

if (isset($_SERVER[‘HTTP_ORIGIN’])) {
header(“Access-Control-Allow-Origin: {$_SERVER[‘HTTP_ORIGIN’]}”);
header(‘Access-Control-Allow-Credentials: true’);
header(‘Access-Control-Max-Age: 86400’); // cache for 1 day
}

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers:        {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

    exit(0);
}

Refering to this link: http://www.nikola-breznjak.com/blog/codeproject/posting-data-from-ionic-app-to-php-server/ and this forum thread: $http No 'Access-Control-Allow-Origin' problem on POST

But i still cant login on my real device…


#17

What’s the full error message you got on the phone? Also, could simply do the following, which is basically the same as what you’re already doing, just neater

header("Access-Control-Allow-Origin: *");

Some people might be really against just allowing all origins though, personally I don’t really understand why that matters, at least in this scenario.

Edit: Either that, or proxy your requests through an origin which is allowed.

#Much later edit, quite important:
Right. Did some searching and reading, so in case anyone stumbles upon this later: You should ONLY have the Access-Control-Allow-Origin: * set when you’re developing using your browser, especially if the API is not only used for the app but also some other web based application! This is not required for the phone, as the files are served from the file:// URI and cross domain policies do not apply. Either you set the ACAO on the server, or setup a proxy in your ionic.config.json file during development

{
  "name": "",
  "app_id": "",
  "v2": true,
  "typescript": true,
  "proxies": [{
    "path": "/api",
    "proxyUrl": "https://example.com/api"
  }]
}

Then call it like this

this.http.get("/api/...", ...)...

405 Method not allowed
#18

I debuged it now with chrome per remote device and on android i get:

Status Code:404 Not Found (from disk cache)

But in Browser i can login??

EDIT:
Sloved it. It was a known issue on android.
I only needed to install:
cordova plugin add cordova-plugin-whitelist
and then in the config.xml add:

<allow-navigation href="http://*/*" />
<allow-navigation href="https://*/*" />
<allow-navigation href="data:*" />

Now i can login on all devices.
So ill change my code to the POST thing now and post it here and update it as solution.


#19

Look just because you did not take it personal doesn’t make it right, all
in saying is that there is a code of conduct, that has been put in place.
It is there Incase some one else finds it harsh so then they are afraid to
ask a question.


#21

Ah thanks for the info i will remove Allow Origin after the developing process is finished!