1

Just started implementing API(get, post mostly) in Flutter. I was getting the post API response well but for GET API, I am getting 401. The code is given below:

import 'package:http/http.dart' as http;

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjgiLCJVc2VyRGV2aWNlSWQiOiI0NTVENDY3Rjc4MjI0QjlDOERFN0JDMjFFNjREQjVEQyIsIkZ1bGxOYW1lIjoiQW5keSBNZW1iZXIiLCJQaG9uZSI6IjA0MjM5OTkzMzMiLCJlbWFpbCI6Im1rYWJpckBmYXFpLmNvbS5hdSIsInN1YiI6IjA0MjM5OTkzMzMiLCJqdGkiOiJkZGY3NDM4OC03YzE4LTQyZjktODdkMC0yMGQzY2NjNDE2ZGIiLCJuYmYiOjE2MjQ5NDIxNzAsImV4cCI6MTYyNDk2Mzc3MCwiaWF0IjoxNjI0OTQyMTcwfQ.I9pR2HZbKlOwl_9Z5O5AE8kZh0o5SdTOxsvTsY28SUk";
var headers = {
'AuthToken': token,
  'accept': 'application/json',
};

 final response = await http.get('http://api.orca.faqdev.com.au/api/account/Dashboard' , headers: headers);

Two things regarding the API :

  1. It is working from the postman
  2. It is working from iOS Swift code.

Anyone can guess what happens here in Flutter? BTW, I am new in Flutter.

Jamshed Alam
  • 12,424
  • 5
  • 26
  • 49
  • is headers param and value you used are same with `Postman` ? – John Joe Jun 27 '21 at 06:27
  • Are you sure the website is up or API working? maybe problem on site side, or check your API token again. – p2kr Jun 27 '21 at 07:15
  • the same token is using postamn! – Jamshed Alam Jun 27 '21 at 08:48
  • There will almost certainly be a discrepancy between the headers Postman/Swift/Flutter implementations are using, perhaps because each is making different assumptions about `Content-Type` etc. Take a good look at the full set of headers each is sending, and compare them. – msbit Jun 27 '21 at 10:09
  • Also confusing things, perhaps, is that this token is now expired (ie the server responds with HTTP status 401 and `Token-Expired: true` header). It would be worth checking again with a refreshed token. – msbit Jun 27 '21 at 10:12
  • Token updated, you can check now on postman. – Jamshed Alam Jun 27 '21 at 10:16
  • 1
    Okay, so running that code, (after fixing the issue with `get` needing `Uri`, not `String`), I can see that Dart sends the following headers: `user-agent: Dart/2.13 (dart:io), accept: application/json, accept-encoding: gzip, authtoken: , content-length: 0` If the API needs more than that, you'll need to add them in as part of the `headers` argument for `get`. – msbit Jun 29 '21 at 06:07
  • I set Uri also, i got the same. I've updated the token just now. Could you please hit it once? I think the reason is tricky/silly. @msbit – Jamshed Alam Jun 29 '21 at 06:24
  • How about you put the headers you are putting into Postman, and the Swift implementation, into the question, to see if there are any discrepancies. – msbit Jun 29 '21 at 06:25

1 Answers1

1

Fundamentally, this comes down to HTTP header names not being case sensitive, and the API server not respecting this.

The header at issue is AuthToken; if sent through verbatim, the server will accept it but if not, the server will reject it. The Dart http package sends through the header in lower case (authtoken) whereas Swift sends through the header maintaining the case (though noting that the names are case insensitive).

Compare:

curl \
  --header 'AuthToken: <redacted>' \
  --silent \
  --verbose \
  http://api.orca.faqdev.com.au/api/account/Dashboard > /dev/null
*   Trying 52.63.81.235...
* TCP_NODELAY set
* Connected to api.orca.faqdev.com.au (52.63.81.235) port 80 (#0)
> GET /api/account/Dashboard HTTP/1.1
> Host: api.orca.faqdev.com.au
> User-Agent: curl/7.64.1
> Accept: */*
> AuthToken: <redacted>
> 
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Server: Microsoft-IIS/10.0
< X-Powered-By: ASP.NET
< Date: Tue, 29 Jun 2021 06:53:33 GMT
< Content-Length: 1697
< 
{ [1697 bytes data]
* Connection #0 to host api.orca.faqdev.com.au left intact
* Closing connection 0

vs:

curl \
  --header 'authtoken: <redacted>' \
  --silent \
  --verbose \
  http://api.orca.faqdev.com.au/api/account/Dashboard > /dev/null
*   Trying 52.63.81.235...
* TCP_NODELAY set
* Connected to api.orca.faqdev.com.au (52.63.81.235) port 80 (#0)
> GET /api/account/Dashboard HTTP/1.1
> Host: api.orca.faqdev.com.au
> User-Agent: curl/7.64.1
> Accept: */*
> authtoken: <redacted>
> 
< HTTP/1.1 401 Unauthorized
< Transfer-Encoding: chunked
< Server: Microsoft-IIS/10.0
< X-Powered-By: ASP.NET
< Date: Tue, 29 Jun 2021 06:54:42 GMT
< 
{ [5 bytes data]
* Connection #0 to host api.orca.faqdev.com.au left intact
* Closing connection 0

The best solution is to configure the API server to treat all headers as case insensitive (as per the spec), then it will work for all clients.

If you absolutely positively can't do that, there is an option to use the HttpClient provided by dart:io, and in that scenario your implementation would end up something like:

import 'dart:convert';
import 'dart:io';

...

final client = HttpClient();
final request = await client
    .getUrl(Uri.parse('http://api.orca.faqdev.com.au/api/account/Dashboard'));
request.headers.set('AuthToken', '<redacted>', preserveHeaderCase: true);
final response = await request.close();
final chunks = await response.transform(utf8.decoder).toList();
final body = chunks.join('');
client.close();
// handle the response
print(body);

Note the use of preserveHeaderCase on the HttpHeaders.set method

msbit
  • 4,152
  • 2
  • 9
  • 22
  • Thought the same, but was not known about the preserveHeaderCase. Awesome job dear @msbit. – Jamshed Alam Jun 29 '21 at 10:05
  • Happy to help, @JamshedAlam. I would like to stress again, that if you are developing the API also, set it up such that the case of headers doesn't matter, it will save you possible heartache in future too :) – msbit Jun 30 '21 at 10:09