iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
1
Mobile Development

iOS Developer Learning Flutter系列 第 19

iOS Developer Learning Flutter. Lesson18 API POST

昨天介紹了一下GET
但是POST就麻煩得多,基本上有以下三種

  1. application/json
  2. application/x-www-form-urlencoded
  3. multipart/form-data

另外在Flutter裡有三種常見的網路請求作法

  1. Flutter dart:io內建的HttpClient
  2. dart出的http套件
  3. 第三方出的dio套件

今天就來測試一下3*3種情境該怎麼寫

Today Preview

測試來源

網路開放資源,可隨意測試

  1. application/json
    http://httpbin.org/delay/1

  2. application/x-www-form-urlencoded
    https://reqres.in/api/login

  3. multipart/form-data
    https://postman-echo.com/post

實驗結果

  1. 網路上查到的說法是說HttpClient不支援multipart/form-data
    然後我的確也找不到該怎麼寫
    所以就空著不管了
  2. 但沒看到HttpClient不支援x-www-form-urlencoded的說法
    我覺得這應該要可以吧
    但還是找不到該怎麼寫
    自己亂寫也打不通
  3. http跟dio是三種寫法都可以
    我就把dio的定位視作Alamofire跟OKHttp☘️☘️☘️
  4. 但在還沒看到http跟dio的差異之前
    我基本會傾向選擇官方的http

code

  1. API
import 'dart:convert';
import 'dart:io';

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

import 'package:idlf/model/User.dart';

typedef UsersCallback = void Function(Users);

class APIManager {

  //以下POST HttpClient分隔線=================

  void loginHttpClientWWW(void Function() success, void Function() fail) async {

    var urlString = "https://reqres.in/api/login";
    HttpClient()
        .postUrl(Uri.parse(urlString))
        .then((HttpClientRequest request) {
      request.headers.contentType = ContentType("application", "x-www-form-urlencoded");
      request.write("{\"email\":\"eve.holt@reqres.in\",\"password\":\"cityslicka\"}");
//      request.write("email=eve.holt@reqres.in");
//      request.write("password=cityslicka");
      return request.close();

    }).then((HttpClientResponse response) {
      if (response.statusCode == 200) {
        response.transform(utf8.decoder).join().then((String string) {
          print("HttpClientWWW Success:$string");
          success();
        });
      } else {
        print("HttpClientWWW Fail:${response.statusCode}");
        fail();
      }
    });
  }

  void loginHttpClientJson(void Function() success, void Function() fail) async {

    var urlString = "http://httpbin.org/delay/1";
    HttpClient()
        .postUrl(Uri.parse(urlString))
        .then((HttpClientRequest request) {
      request.headers.contentType = ContentType("application", "json");

      Map<String,String> p = {"UserName":"eve.holt@reqres.in", "Password":"cityslicka"};
      request.add(utf8.encode(json.encode(p)));
      return request.close();

    }).then((HttpClientResponse response) {
      if (response.statusCode == 200) {
        response.transform(utf8.decoder).join().then((String string) {
          print("HttpClientJson Success:$string");
          success();
        });
      } else {
        print("HttpClientJson Fail:${response.statusCode}");
        fail();
      }
    });
  }

  void loginHttpClientForm(void Function() success, void Function() fail) async {

  }

  //以下POST http分隔線=================

  void loginHttpWWW(void Function() success, void Function() fail) async {

    var urlString = "https://reqres.in/api/login";
    Map<String, String> headersMap = new Map();
    headersMap["content-type"] = "application/x-www-form-urlencoded";

    Map<String, String> bodyParams = new Map();
    bodyParams["email"] = "eve.holt@reqres.in";
    bodyParams["password"] = "cityslicka";
    http.Client()
        .post(urlString, headers: headersMap, body: bodyParams, encoding: Utf8Codec())
        .then((http.Response response) {
      if (response.statusCode == 200) {
        print("httpWWW Success:${response.body}");
        success();
      } else {
        print("httpWWW Fail:${response.statusCode}");
        fail();
      }
    }).catchError((error) {
      print("httpWWW Fail:${error.toString()}");
      fail();
    });
  }

  void loginHttpJson(void Function() success, void Function() fail) async {

    var urlString = "http://httpbin.org/delay/1";
    Map<String, String> headersMap = new Map();
    headersMap["content-type"] = ContentType.json.toString();

    Map<String, String> bodyParams = new Map();
    bodyParams["email"] = "eve.holt@reqres.in";
    bodyParams["password"] = "cityslicka";
    http.Client()
        .post(urlString, headers: headersMap, body: jsonEncode(bodyParams), encoding: Utf8Codec())
        .then((http.Response response) {
      if (response.statusCode == 200) {
        print("httpJson Success:${response.body}");
        success();
      } else {
        print("httpJson Fail:${response.statusCode}");
        fail();
      }
    }).catchError((error) {
      print("httpJson Fail:${error.toString()}");
      fail();
    });
  }

  void loginHttpForm(void Function() success, void Function() fail) async {

    var urlString = "https://postman-echo.com/post";
    var client = new http.MultipartRequest("post", Uri.parse(urlString));
    client.fields["email"] = "eve.holt@reqres.in";
    client.fields["password"] = "cityslicka";
    client.send().then((http.StreamedResponse response) {
      if (response.statusCode == 200) {
        response.stream.transform(utf8.decoder).join().then((String string) {
          print("httpForm Success:$string");
          success();
        });
      } else {
        print("httpForm Fail:${response.statusCode}");
        fail();
      }
    }).catchError((error) {
      print("httpForm Fail:${error.toString()}");
      fail();
    });
  }

  //以下POST Dio分隔線=================

  void loginDioWWW(void Function() success, void Function() fail) async {

    var urlString = "https://reqres.in/api/login";
    try {
      Response response = await Dio()
        .post(urlString,
          data: {"email":"eve.holt@reqres.in", "password":"cityslicka"},
          options: Options(contentType:Headers.formUrlEncodedContentType));
      print("DioWWW Success:${response.data}");
      success();
    } catch (error) {
      print("DioWWW Fail:${error.toString()}");
      fail();
    }
  }

  void loginDioJson(void Function() success, void Function() fail) async {

    var urlString = "http://httpbin.org/delay/1";
    try {
      Response response = await Dio()
          .post(urlString,
          data: {"email":"eve.holt@reqres.in", "password":"cityslicka"},
          options: Options(contentType:Headers.jsonContentType));
      print("DioJson Success:${response.data}");
      success();
    } catch (error) {
      print("DioJson Fail:${error.toString()}");
      fail();
    }
  }

  void loginDioForm(void Function() success, void Function() fail) async {

    var urlString = "https://postman-echo.com/post";
    try {
      Response response = await Dio()
          .post(urlString,
          data: FormData.fromMap({"email":"eve.holt@reqres.in", "password":"cityslicka"}));
      print("DioForm Success:${response.data}");
      success();
    } catch (error) {
      print("DioForm Fail:${error.toString()}");
      fail();
    }
  }


}
  1. Widget

class LessonPageApiPost extends StatefulWidget {
  @override
  _LessonPageApiPostState createState() => _LessonPageApiPostState();
}

class _LessonPageApiPostState extends State<LessonPageApiPost> {

  final long = 66.0;
  final List<bool> formDataResults = [null, null, null];
  final List<bool> jsonResults = [null, null, null];
  final List<bool> wwwResults = [null, null, null];
  
  Widget _createContainer(String text) {
    return Container(
      height: long,
      alignment: Alignment.center,
      child: Text(text),
    );
  }

  Widget _createResultWidget(bool result) {

    if (result == true) {
      return _createContainer("⭕️");
    } else if (result == false) {
      return _createContainer("❌");
    } else {
      return Container(
          height: long,
          child: CupertinoActivityIndicator()
      );
    }
  }

  Widget _createTable() {

    return Table(

      columnWidths: {
        1: FixedColumnWidth(long),
        2: FixedColumnWidth(long),
        3: FixedColumnWidth(long),
      },

      children: [
        TableRow(
            children: [
              _createContainer(""),
              _createContainer("HttpClient"),
              _createContainer("http"),
              _createContainer("Dio"),
            ]
        ),

        TableRow(
            children: [
              _createContainer("json"),
              _createResultWidget(jsonResults[0]),
              _createResultWidget(jsonResults[1]),
              _createResultWidget(jsonResults[2]),
            ]
        ),

        TableRow(
            children: [
              _createContainer("x-www-form-urlencoded"),
              _createResultWidget(wwwResults[0]),
              _createResultWidget(wwwResults[1]),
              _createResultWidget(wwwResults[2]),
            ]
        ),

        TableRow(
            children: [
              _createContainer("form-data"),
              _createResultWidget(formDataResults[0]),
              _createResultWidget(formDataResults[1]),
              _createResultWidget(formDataResults[2]),
            ]
        )
      ]
    );
  }

  @override
  void initState() {
    super.initState();

    //=============www
    APIManager().loginHttpClientWWW(() {
      setState(() { wwwResults[0] = true; });
    }, () {
      setState(() { wwwResults[0] = false; });
    });

    APIManager().loginHttpWWW(() {
      setState(() { wwwResults[1] = true; });
    }, () {
      setState(() { wwwResults[1] = false; });
    });

    APIManager().loginDioWWW(() {
      setState(() { wwwResults[2] = true; });
    }, () {
      setState(() { wwwResults[2] = false; });
    });

    //=============json
    APIManager().loginHttpClientJson(() {
      setState(() { jsonResults[0] = true; });
    }, () {
      setState(() { jsonResults[0] = false; });
    });

    APIManager().loginHttpJson(() {
      setState(() { jsonResults[1] = true; });
    }, () {
      setState(() { jsonResults[1] = false; });
    });

    APIManager().loginDioJson(() {
      setState(() { jsonResults[2] = true; });
    }, () {
      setState(() { jsonResults[2] = false; });
    });

    //=============Form
    APIManager().loginHttpClientForm(() {
      setState(() { formDataResults[0] = true; });
    }, () {
      setState(() { formDataResults[0] = false; });
    });

    APIManager().loginHttpForm(() {
      setState(() { formDataResults[1] = true; });
    }, () {
      setState(() { formDataResults[1] = false; });
    });

    APIManager().loginDioForm(() {
      setState(() { formDataResults[2] = true; });
    }, () {
      setState(() { formDataResults[2] = false; });
    });

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("第十六堂課"),
      ),
      body: Padding(
        padding: EdgeInsets.only(right: 8),
        child: _createTable()
      )
    );
  }
}

參考連結


本集內容Android版請見:iOS Developer Learning Android. Lesson 18

下集預告:生命週期

最後提供一下github.com/mark33699/IDLF


上一篇
iOS Developer Learning Flutter. Lesson17 API GET
下一篇
iOS Developer Learning Flutter. Lesson19 Life Cycle
系列文
iOS Developer Learning Flutter30

尚未有邦友留言

立即登入留言