Flutter http get api failed, may be for wrong header style

247

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

Share:
247
Jamshed Alam
Author by

Jamshed Alam

I am an iOS developer with 7+ years of professional experience. I enjoy developing the latest work on many projects and always heartily try to learn about new technology. I am working with Xcode, Objective-C, Swift, and Cocos2d. I am interested in best practices that are being adopted by the iOS community and implementing them into my work. I have developed productivity, lifestyle, utility-related apps, and many more. Professional Expertise: Languages: Objective-C, Swift. iOS Frameworks/API: iOS 6.00-iOS 15.0, UIKit, SwiftUI, AVFoundation, CoreLocation, CoreGraphics, CoreAnimation, MapKit, RESTKit, AFNetworking, MKStoreKit, GCD, Thread, Custom UI Elements, Auto Layout, CocoaPods, SQLite, Core Data, iAD, GoogleMobileAds, SDWebImage, File System, Contact, CloudKit, Push/Local Notification, In-App Purchase and everything related to app store submission. IDE: Xcode. Source Control: Git. Find me on LinkedIn , Facebook , Skype : codetolive99 App Links : SelfieYo Chat Burn 2 Learn , Fitness app HungryNaki - Food Delivery App Email me at [email protected]

Updated on December 30, 2022

Comments

  • Jamshed Alam
    Jamshed Alam over 1 year

    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.

    • John Joe
      John Joe almost 3 years
      is headers param and value you used are same with Postman ?
    • p2kr
      p2kr almost 3 years
      Are you sure the website is up or API working? maybe problem on site side, or check your API token again.
    • Jamshed Alam
      Jamshed Alam almost 3 years
      the same token is using postamn!
    • msbit
      msbit almost 3 years
      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
      msbit almost 3 years
      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.
    • Jamshed Alam
      Jamshed Alam almost 3 years
      Token updated, you can check now on postman.
    • msbit
      msbit almost 3 years
      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: <token>, 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.
    • Jamshed Alam
      Jamshed Alam almost 3 years
      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
    • msbit
      msbit almost 3 years
      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.
  • Jamshed Alam
    Jamshed Alam almost 3 years
    Thought the same, but was not known about the preserveHeaderCase. Awesome job dear @msbit.
  • msbit
    msbit almost 3 years
    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 :)