Flutter & Dart: Call RESTful API and Authen with JWT
Published: 2021-04-29 11:16:46

#### Agenda
* Call REST API with GET Method
* Call REST API with POST Method
* JSON Serialization (jsonEncode)
* JSON Deserializaton (jsonDecode)
* Call Authorized REST API with JWT

####Prerequisite
* [Flutter 2.x+](https://flutter.dev)
* [Dart 2.1x+ with Null safety](https://dart.dev)
* [http 0.13.x+ package](https://pub.dev/packages/http)

####1. สร้างโปรเจ็ค แล้ว migrate ให้ dart ซัพพอร์ท null safety 
```sh
dart create rest_api
cd rest_api
dart migrate --apply-changes
```

####2. ติดตั้ง http package 
```sh
dart pub add http
```
เข้าไปดูได้ที่ [https://pub.dev/packages/http](https://pub.dev/packages/http)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dqved817827s0tgas3ws.png)

####3. เตรียม API 
ผมทำเอาไว้เพื่อทดสอบแล้วที่ [https://techcoach.azurewebsites.net](https://techcoach.azurewebsites.net)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s5jc0amora3hm6ul4jlz.png)


####4. ตัวอย่างที่ 1 การเรียก API แบบ GET กับการดึงค่า Gender
URL: [https://techcoach.azurewebsites.net/registration/genders](https://techcoach.azurewebsites.net/registration/genders)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fo3n5cxnfpu85k0l538h.png)

#####4.1 import http,io,convert package แล้วสร้างฟังชั่น getGender แบบ async 

```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

void main(List<String> arguments) {
  
}

Future getGenders() async {

}
```

#####4.2 เขียนโปรแกรมที่ฟังชั่น getGenders เรียกฟังชั่น http.get แล้วส่ง URL เข้าไปดังนี้
```dart
final response = await http.get(
  Uri.parse('https://techcoach.azurewebsites.net/registration/genders'),
);
```

#####4.2 ตรวจสอบว่าถ้าสถานะไม่ใช่ ok (200) ก็จะหยุดการทำงาน
```dart
if (response.statusCode != HttpStatus.ok) {
  print('Response: ${response.statusCode}');
  return;
}
```

#####4.3 พิมพ์ผลที่ได้ออกมาดูก่อน
```dart
print(response.body);
```

#####4.3 เปลี่ยนที่ฟังชั่น main เป็นแบบ async แล้วเรียกฟังชั่น getGenders
```dart
void main(List<String> arguments) async {
  await getGenders();
}
```

ผลลัพธ์ที่ได้หลังจากการรันคำสั่ง 
```sh
dart run
[{"id":1,"name":"ชาย"},{"id":2,"name":"หญิง"}]
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9g6jv413eu2vq6je31xe.png)

#####4.4 สร้างคลาส Gender โดยมี id กับ name เป็น arribute พร้อมกับสร้าง constructor เพื่อรับค่าด้วย
```dart
class Gender {
  final int id;
  final String name;
  Gender({
    required this.id,
    required this.name,
  });
}
```

#####4.4 เพิ่ม factory constructor ให้กับ Gender เพื่อความสะดวกในการแปลงจาก Map เป็น Object
```dart
class Gender {
  final int id;
  final String name;
  Gender({
    required this.id,
    required this.name,
  });

  factory Gender.fromJson(Map<String, dynamic> json) => Gender(
        id: json['id'],
        name: json['name'],
      );
}
```

#####4.5 แก้ไขฟังชั่น getGenders ลบบรรทัด print ออกแล้วเพิ่มคำสั่ง jsonDecode เพื่อแปลงจาก JSON String ให้เป็น List ของ Map 
```dart
final json = jsonDecode(response.body) as List;
```

#####4.6 ใช้ map เพื่อแปลงจาก Map ให้กลายเป็น List ของ Gender
```dart
final genders = json.map((e) => Gender.fromJson(e)).toList();
```

#####4.6 แสดงผลที่ได้จาก List ของ Gender
```dart
for (var gender in genders) {
  print('id: ${gender.id}, name: ${gender.name}');
}
```

ผลลัพธ์ที่ได้หลังจากการรันคำสั่ง 
```sh
dart run
id: 1, name: ชาย
id: 2, name: หญิง
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zeljrqaqeucvmv0oket.png)
โค้ดทั้งหมด
```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

void main(List<String> arguments) async {
  await getGenders();
}

Future getGenders() async {
  final response = await http.get(
    Uri.parse('https://techcoach.azurewebsites.net/registration/genders'),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  final json = jsonDecode(response.body) as List;
  final genders = json.map((e) => Gender.fromJson(e)).toList();
  for (final gender in genders) {
    print('id: ${gender.id}, name: ${gender.name}');
  }
}

class Gender {
  int id;
  String name;
  Gender({
    required this.id,
    required this.name,
  });

  factory Gender.fromJson(Map<String, dynamic> json) => Gender(
        id: json['id'],
        name: json['name'],
      );
}
```

####5. ตัวอย่างที่ 2 การเรียก API แบบ POST กับการลงทะเบียน User โดยจะต้องส่ง payload ในรูปแบบ JSON ที่ body ด้วย
URL: [https://techcoach.azurewebsites.net/registration/users](https://techcoach.azurewebsites.net/registration/users)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydvsikxgaitn2kmsw405.png)

#####5.1 สร้างฟังชั่น signup แบบ async รับพารามิเตอร์ที่ต้องการ แล้วเรียกฟังชั่น http.post แล้วส่ง URL เข้าไปดังนี้
```dart
Future signup({
  required final String firstName,
  required final String lastName,
  required final String email,
  required final String password,
  required final int genderId,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
  );
}
```

#####5.2 เพิ่ม content-type: application/json เข้าไปที่ headers 
```dart
final response = await http.post(
  Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
  headers: {'content-type': 'application/json'},
);
```

#####5.3 ที่ body สร้าง Map ของคีย์ต่างๆที่ payload ต้องการ แล้วใช้ jsonEncode เพื่อแปลงจาก Map ให้เป็น JSON String
```dart
final response = await http.post(
  Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
  headers: {'content-type': 'application/json'},
  body: jsonEncode({
    'firstName': firstName,
    'lastName': lastName,
    'email': email,
    'password': password,
    'genderId': genderId,
  }),
);
```

#####5.4 ตรวจสอบว่าถ้าสถานะ ok (200) แสดงผลคำว่า signup success
```dart
if (response.statusCode != HttpStatus.ok) {
  print('Response: ${response.statusCode}');
  return;
}

print('signup success');
```

#####5.5 ที่ฟังชั่น main ให้เรียกฟังชั่น signup ตัวอย่างดังนี้
```dart
void main(List<String> arguments) async {
  await signup(
    firstName: 'Code',
    lastName: 'Bangkok',
    email: 'codebangkok@gmail.com',
    password: 'iloveyou',
    genderId: 1,
  );
}
```

ผลลัพธ์ที่ได้หลังจากการรันคำสั่ง 
```sh
dart run
signup success
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yv4z6dqrgyu71f6yyke7.png)

โค้ดที่เกี่ยวข้องทั้งหมด
```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

void main(List<String> arguments) async {
  await signup(
    firstName: 'Code',
    lastName: 'Bangkok',
    email: 'codebangkok@gmail.com',
    password: 'iloveyou',
    genderId: 1,
  );
}

Future signup({
  required final String firstName,
  required final String lastName,
  required final String email,
  required final String password,
  required final int genderId,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
    headers: {'content-type': 'application/json'},
    body: jsonEncode({
      'firstName': firstName,
      'lastName': lastName,
      'email': email,
      'password': password,
      'genderId': genderId,
    }),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  print('signup success');
}
```

####6. ตัวอย่างที่ 3 การเรียก API แบบ POST กับการ Login โดยจะต้องส่ง payload ในรูปแบบ JSON ที่ body และรับค่า response กลับมาด้วย
URL: [https://techcoach.azurewebsites.net/registration/login](https://techcoach.azurewebsites.net/registration/login)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddp85uxtcfitztyxxv55.png)

#####6.1 สร้างฟังชั่น login แบบ async รับพารามิเตอร์ที่ต้องการ แล้วเรียกฟังชั่น http.post แล้วส่ง URL เข้าไปดังนี้
```dart
Future login({
  required final String email,
  required final String password,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/login'),
  );
}
```

#####6.2 เพิ่ม content-type: application/json เข้าไปที่ headers 
```dart
final response = await http.post(
  Uri.parse('https://techcoach.azurewebsites.net/registration/login'),
  headers: {'content-type': 'application/json'},
);
```

#####6.3 ที่ body สร้าง Map ของคีย์ต่างๆที่ payload ต้องการ แล้วใช้ jsonEncode เพื่อแปลงจาก Map ให้เป็น JSON String
```dart
final response = await http.post(
  Uri.parse('https://techcoach.azurewebsites.net/registration/login'),
  headers: {'content-type': 'application/json'},
  body: jsonEncode({
    'email': email,
    'password': password,
  }),
);
```

#####6.4 ตรวจสอบว่าถ้าสถานะไม่ใช่ ok (200) ก็จะหยุดการทำงาน
```dart
if (response.statusCode != HttpStatus.ok) {
  print('Response: ${response.statusCode}');
  return;
}
```

#####6.5 สร้างคลาส User โดยมี arribute ต่างๆตาม response พร้อมกับสร้าง constructor เพื่อรับค่าด้วย
```dart
class User {
  final int id;
  final String firstName;
  final String lastName;
  final String email;
  final Gender gender;
  final String jwtToken;
  User({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.gender,
    required this.jwtToken,
  });
}
```

#####6.6 เพิ่ม factory constructor ให้กับ User เพื่อความสะดวกในการแปลงจาก Map เป็น Object
```dart
class User {
  final int id;
  final String firstName;
  final String lastName;
  final String email;
  final Gender gender;
  final String jwtToken;
  User({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.gender,
    required this.jwtToken,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
        id: json['id'],
        firstName: json['firstName'],
        lastName: json['lastName'],
        email: json['email'],
        gender: Gender.fromJson(json['gender']),
        jwtToken: json['jwtToken'],
      );
}
```

#####6.7 แก้ไขฟังชั่น login เพิ่มคำสั่ง jsonDecode เพื่อแปลงจาก JSON String ไปเป็น Map 
```dart
final json = jsonDecode(response.body);
```

#####6.8 แปลงจาก Map ให้กลายเป็น User object ด้วย fromJson 
```dart
final user = User.fromJson(json);
```

#####6.9 แสดงผลที่ได้จาก User object
```dart
print('First Name: ${user.firstName}');
print('Last Name: ${user.lastName}');
print('Email: ${user.email}');
print('Gender: ${user.gender.name}');
print('JWT Token: ${user.jwtToken}');
```

#####6.10 ที่ฟังชั่น main ให้เรียกฟังชั่น login ตัวอย่างดังนี้
```dart
void main(List<String> arguments) async {
  await login(
    email: 'codebangkok@gmail.com',
    password: 'iloveyou',
  );
}
```

ผลลัพธ์ที่ได้หลังจากการรันคำสั่ง (jwt token จะเปลี่ยนทุกครั้งที่รัน)
```sh
dart run
First Name: Code
Last Name: Bangkok
Email: codebangkok@gmail.com
Gender: ชาย
JWT Token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjYiLCJzdWIiOiJjb2RlYmFuZ2tva0BnbWFpbC5jb20iLCJlbWFpbCI6ImNvZGViYW5na29rQGdtYWlsLmNvbSIsIm5iZiI6MTYxOTY5MDkwMSwiZXhwIjoxNjE5NzEyNTAxLCJpYXQiOjE2MTk2OTA5MDF9.BBQ6RDSIZodGdn19Af5HrZLHSyGOJJReXAsSXfN2LG1dc4kTK6SlLc6U7e65-9iSBqiM4MA97EMvJvRUYKbUwQ
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlp6rym6bb855zwn9ayp.png)

โค้ดที่เกี่ยวข้องทั้งหมด
```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

void main(List<String> arguments) async {
  await login(
    email: 'codebangkok@gmail.com',
    password: 'iloveyou',
  );
}

Future login({
  required final String email,
  required final String password,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/login'),
    headers: {'content-type': 'application/json'},
    body: jsonEncode({
      'email': email,
      'password': password,
    }),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  final json = jsonDecode(response.body);
  final user = User.fromJson(json);
  print('First Name: ${user.firstName}');
  print('Last Name: ${user.lastName}');
  print('Email: ${user.email}');
  print('Gender: ${user.gender.name}');
  print('JWT Token: ${user.jwtToken}');
}

class User {
  final int id;
  final String firstName;
  final String lastName;
  final String email;
  final Gender gender;
  final String jwtToken;
  User({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.gender,
    required this.jwtToken,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
        id: json['id'],
        firstName: json['firstName'],
        lastName: json['lastName'],
        email: json['email'],
        gender: Gender.fromJson(json['gender']),
        jwtToken: json['jwtToken'],
      );
}
```

####7. ตัวอย่างที่ 4 การเรียก Authorized API แบบ GET ด้วยการใช้ JWT Token
URL: [https://techcoach.azurewebsites.net/registration/users](https://techcoach.azurewebsites.net/registration/users)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pdl5n4rd6m5qv7gy8gd.png)

#####7.1 สร้างฟังชั่น getUsers แบบ async รับ jwt token เข้ามา แล้วเรียกฟังชั่น http.get แล้วส่ง URL เข้าไปดังนี้
```dart
Future getUsers({required String jwtToken}) async {
  final response = await http.get(
    Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
  );
}

```

#####7.2 เพิ่ม Authorization: Bearer <jwtToken> เข้าไปที่ headers นำค่า jwt token ที่ได้จากการ login มาใส่
```dart
final response = await http.get(
  Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
  headers: {'authorization': 'Bearer $jwtToken'},
);
```

#####7.3 ตรวจสอบว่าถ้าสถานะ ok (200) ก็พิมพ์ผลลัพธ์ที่ได้ออกมาดู
```dart
if (response.statusCode != HttpStatus.ok) {
  print('Response: ${response.statusCode}');
  return;
}

print(response.body);
```

#####7.4 ที่ฟังชั่น main ให้เรียกฟังชั่น getUsers ตัวอย่างดังนี้
```dart
void main(List<String> arguments) async {
  final jwtToken = ''; //นำค่า jwt ที่ได้จากการ login มาใส่ที่นี่
  await getUsers(jwtToken: jwtToken);
}
```

ผลลัพธ์ที่ได้หลังจากการรันคำสั่ง
```sh
dart run
[{"id":1,"firstName":"Code","lastName":"Bangkok","email":"codebangkok@gmail.com","gender":{"id":1,"name":"ชาย"}}]
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5syznrnpey21kjep02u.png)

โค้ดทั้งหมด
```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

void main(List<String> arguments) async {
  final jwtToken = ''; //นำค่า jwt ที่ได้จากการ login มาใส่ที่นี่
  await getUsers(jwtToken: jwtToken);
}

Future getUsers({required String jwtToken}) async {
  final response = await http.get(
    Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
    headers: {'authorization': 'Bearer $jwtToken'},
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  print(response.body);
}

Future login({
  required final String email,
  required final String password,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/login'),
    headers: {'content-type': 'application/json'},
    body: jsonEncode({
      'email': email,
      'password': password,
    }),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  final json = jsonDecode(response.body);
  final user = User.fromJson(json);
  print('First Name: ${user.firstName}');
  print('Last Name: ${user.lastName}');
  print('Email: ${user.email}');
  print('Gender: ${user.gender.name}');
  print('JWT Token: ${user.jwtToken}');
}

Future signup({
  required final String firstName,
  required final String lastName,
  required final String email,
  required final String password,
  required final int genderId,
}) async {
  final response = await http.post(
    Uri.parse('https://techcoach.azurewebsites.net/registration/users'),
    headers: {'content-type': 'application/json'},
    body: jsonEncode({
      'firstName': firstName,
      'lastName': lastName,
      'email': email,
      'password': password,
      'genderId': genderId,
    }),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  print('signup success');
}

Future getGenders() async {
  final response = await http.get(
    Uri.parse('https://techcoach.azurewebsites.net/registration/genders'),
  );

  if (response.statusCode != HttpStatus.ok) {
    print('Response: ${response.statusCode}');
    return;
  }

  final json = jsonDecode(response.body) as List;
  final genders = json.map((e) => Gender.fromJson(e)).toList();
  for (final gender in genders) {
    print('id: ${gender.id}, name: ${gender.name}');
  }
}

class Gender {
  final int id;
  final String name;
  Gender({
    required this.id,
    required this.name,
  });

  factory Gender.fromJson(Map<String, dynamic> json) => Gender(
        id: json['id'],
        name: json['name'],
      );
}

class User {
  final int id;
  final String firstName;
  final String lastName;
  final String email;
  final Gender gender;
  final String jwtToken;
  User({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.email,
    required this.gender,
    required this.jwtToken,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
        id: json['id'],
        firstName: json['firstName'],
        lastName: json['lastName'],
        email: json['email'],
        gender: Gender.fromJson(json['gender']),
        jwtToken: json['jwtToken'],
      );
}
```

####ติดตามผลงานได้ที่
* **Page:** [https://fb.com/CodeBangkok​](https://fb.com/CodeBangkok​)
* **Group:** [https://fb.com/groups/msdevth​](https://fb.com/groups/msdevth​)
* **Blog:** [https://dev.to/codebangkok](https://dev.to/codebangkok)
* **YouTube:** [https://youtube.com/CodeBangkok](https://youtube.com/CodeBangkok)