跳至主要內容

http

chanchaw大约 4 分钟flutter

依赖

在项目依赖配置文件 pubspec.yaml 中添加如下的 http 依赖,类似 maven 添加依赖,记得执行 pub get 拉取依赖到项目源码

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.5

规范

实体类

制作实体类 lib\entity\repair_main_dto.dart,同 java 规范不同,单词都使用小写,以下划线作为分隔符,模板代码如下,大体类似 java 实体类

  • 构造函数 - 和类同名的方法 RepairMainDto

  • map 数据转实体类 - 方法 RepairMainDto.fromJson

  • 实体类转 map 数据 - 方法 toJson

  • toString 方法

// RepairMainDto = java后台实体类 RepairMaindto
// 此处只设置用得到的属性 progress
class RepairMainDto {
  int? id;// repair_main 主键字段 id
  String? progress;// repair_main 字段 progress

  // 构造方法
  RepairMainDto({this.id,this.progress});
  // 静态构造方法,将 http 请求的响应数据转换为实体类对象
  factory RepairMainDto.fromJson(Map<String,dynamic> map){
    return RepairMainDto(
      id: map['id'],
      progress: map['progress'],
    );
  }

  Map<String,dynamic> toJson(){
    Map<String,dynamic> ret = {};
    ret['id'] = id;
    ret['progress'] = progress;
    return ret;
  }

  
  String toString() {
    return 'RepairMainDto:{id:$id,progress:$progress}';
  }
}

dao

类似 javadao,不同处在于 javadao 负责和数据库通信,这里的 dao 也是获取数据,不过是通过发送 http 请求获取数据。制作类 lib\dao\repair_main_dao.dart 代码如下

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:snbc_ems_pda_annex/entity/repair_main_dto.dart';

class RapairMainDao {
  static Future<RepairMainDto> fetch({required num id}) async {
    // 请求报修记录
    String uri = 'http://192.168.110.200/snbcemsbe/repairMain/selectBydto/$id';
    var url = Uri.parse(uri);
    final res = await http.post(url);
    print(res.toString());
    Utf8Decoder utf8decoder = const Utf8Decoder();
    String bodyStr = utf8decoder.convert(res.bodyBytes);
    if(res.statusCode != 200) throw Exception(bodyStr);

    var map = jsonDecode(bodyStr);// 开发者后台响应来的响应结果,.data 是实际的数据
    print(map);
    return RepairMainDto.fromJson(map['data']);
  }
}

调用

最后在业务组件中调用 dao

import 'package:snbc_ems_pda_annex/utils/device_utils.dart';
import 'package:snbc_ems_pda_annex/dao/repair_main_dao.dart';

Future<void> _getRepairMain() async {
  final RepairMainDto repairMainDto = await RapairMainDao.fetch(id: 7);
  print(repairMainDto);
}

最简案例

新创建的项目在入口文件 main.dart 的入口方法 build 的属性 home 后面填写自己制作的 widget ,填写后的 main.dart 的完整代码如下

import 'package:flutter/material.dart';
import 'package:netstorage/http.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const HttpStudy(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

下面是自己制作的演示 http 用法的代码

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

class HttpStudy extends StatefulWidget {
  const HttpStudy({super.key});

  
  State<HttpStudy> createState() => _HttpStudyState();
}

class _HttpStudyState extends State<HttpStudy> {
  var httpRes = '';
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('演示Http网络请求')),
      body: Column(children: [
        _doGetBtn(),
        Text('htt请求来的响应:$httpRes')
      ],),
    );
  }

  // 创建按钮
  _doGetBtn(){
    return ElevatedButton(onPressed: _doGet, child: const Text('发送GET请求'));
  }
  _doGet() async {
    print('_doGet');
    var uri = Uri.parse('https://showawx.xdfznh.club/visitorbe/test/now');
    var res = await http.get(uri);
    print('请求得到的响应:${res.toString()}');
    if(res.statusCode == 200){
      // res.body 是开发人员后台响应的数据
      print('请求成功,body:${res.body}');
      setState(() {
        httpRes = res.body;
      });
    }else{
      setState(() {
        httpRes = '请求失败,code:${res.statusCode},body:${res.body}';
      });
    }
  }
}

post请求

后端接受 json 类型的参数则需要在 body 中转换为 json 形式。如果是 x-www-form-urlencoded 类型则不需要 jsonEncode (json 时此处是 application/json),注意通过 headers 指定是 json 形式

  _doPost() async {
    var uri = Uri.parse('https://www.xdfznh.club/wxpadbe/dev/proxyUrl');
    var params = {'rawUrl':'https://www.xdfznh.club'};
    var res = await http.post(
        uri,
        body: jsonEncode(params),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8', // 关键:设置 Content-Type
        },
    );
    if(res.statusCode == 200){
      print('请求来的响应:${res.body}');
      setState(() {
        postRes = res.body;
      });
    }else{
      setState(() {
        postRes = 'post请求异常,code:${res.statusCode},msg:${res.body}';
      });
    }
  }

json反序列化为map

注意下面 jsonDecode 后的 map 对象要使用中括号获取属性的值

  _doGet() async {
    print('_doGet');
    var uri = Uri.parse('https://showawx.xdfznh.club/visitorbe/test/now');
    var res = await http.get(uri);
    print('请求得到的响应:${res.toString()}');
    if(res.statusCode == 200){
      // res.body 是开发人员后台响应的数据
      print('请求成功,body:${res.body}');
      setState(() {
        getRes = res.body;
      });

      // json 数据反序列化
      var data = jsonDecode(res.body);
      print('data:${data}');

      var success = data['success'];
      var msg = data['msg'];
      print('success:$success,msg:$msg');
    }else{
      setState(() {
        getRes = '请求失败,code:${res.statusCode},body:${res.body}';
      });
    }
  }

处理响应结果的中文

下面是登录请求,最后几行处理响应结果的中文

  static login({required String username, required String password}) async {
    Map<String,String> reqData = {'email':'chanchaw@126.com','password':'eVF4TJ'};
    var uri = Uri.parse(url);
    var res = await http.post(
      uri,
      body: jsonEncode(reqData),
      headers: {
        'Content-Type': 'application/json; charset=UTF-8', // 关键:设置 Content-Type
      },
    );

    if(res.statusCode == 200) print('请求来的响应:${res.body}');
    else print('登录请求异常,code:${res.statusCode},msg:${res.body}');

    // 处理中文
    Utf8Decoder utf8decoder = const Utf8Decoder();
    String bodyString = utf8decoder.convert(res.bodyBytes);
    print('处理中文后的响应结果:$bodyString');
  }

DAO模板代码

http 请求获取数据的步骤如下:

  1. 请求 http
  2. 使用 utf8 处理中文
  3. 响应 http 状态码200则返回字符串结果,由业务组件解析为实体类
  4. 没有权限 401 则跳转到登录页面
  5. 其他错误码则抛出提示
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:netstorage/utils/navigator_utils.dart';

class HomeDao {
  // 请求首页数据
  static Future<String?> fetch() async {
    var url = Uri.parse('请求主页数据的URI');
    final res = await http.get(url);
    Utf8Decoder utf8decoder = const Utf8Decoder();
    String bodyStr = utf8decoder.convert(res.bodyBytes);
    print('请求首页数据的响应结果:$bodyStr');
    if(res.statusCode == 200) return bodyStr;
    if(res.statusCode == 401) {
      NavigatorUtils.goLogin();
      return null;
    }
    throw Exception(bodyStr);
  }
}