/  HTTP  |   HTTPS  /

HTTP是一个客户端(用户)和 服务端(网站)之间请求和应答的标准,通常使用TCP协议。客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。客户端 (用户代理程序) 向应答服务器 (源服务器) 发起请求 , 从服务器获取需要的资源 (包括 : 文件、图像 、文本、视频 等等) 。客户端和服务端之间 可能存在多个中间层 (例如 : 代理服务器、网关) 。HTTP可以在任何互联网协议或其他网络上实现 , 使用TCP作为其传输层 。

请求方式

GET 请求

向服务端发起请求用于从服务端读取数据 。浏览器发出的GET只能由一个url触发。GET上要在url之外带一些参数就只能依靠url上附带querystring。

使用场景

例如: https://host:port/path?querystring=value1&queryString=value2

https://tieba.baidu.com/f?ie=utf-8&kw=%E5%A4%A7%E4%BD%AC&fr=searchhttps://tieba.baidu.com/f?ie=utf-8&kw=%E5%A4%A7%E7%A5%9E&fr=searchhttps://tieba.baidu.com/f?ie=utf-8&kw=%E7%BE%8E%E5%A5%B3&fr=searchhttps://tieba.baidu.com/f?ie=utf-8&kw=%E5%B8%85%E5%93%A5&fr=search
class MyHomePage extends StatefulWidget {MyHomePage({Key? key, required this.title}) : super(key: key);final String title;@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {void _testDioHeadReq() async {Response _headReq = await Dio().get('https://www.baidu.com/');print('HEAD请求获取到到 数据:${_headReq.data}\n');print('HEAD请求获取到到 extra:${_headReq.extra}\n');print('HEAD请求获取到到 headers:${_headReq.headers}\n');print('HEAD请求获取到到 isRedirect:${_headReq.isRedirect}\n');print('HEAD请求获取到到 statusCode:${_headReq.statusCode}\n');print('HEAD请求获取到到 statusMessage:${_headReq.statusMessage}\n');}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),floatingActionButton: FloatingActionButton(onPressed: _testDioHeadReq,tooltip: 'TestDioHeadReq',child: Icon(Icons.add),),);}
}

HEAD

向服务端发起请求用于从服务端读取数据 。与GET的区别就是不会获取到数据 (响应体) , 只会获取到请求头 (header) 、状态码 (statusCode)、提示信息 (statusMessage) 等 响应头

HEAD请求常常被忽略,但是能提供很多有用的信息,特别是在有限的速度和带宽下。主要有以下特点:只请求资源的首部 、检查超链接的有效性 、检查网页是否被修改

多用于自动搜索机器人获取网页的标志信息,获取rss种子信息,或者传递安全认证信息等

Response _headReq = await Dio().head('https://www.baidu.com/');
print('HEAD请求获取到到 数据:${_headReq.data}\n');
print('HEAD请求获取到到 extra:${_headReq.extra}\n');
print('HEAD请求获取到到 headers:${_headReq.headers}\n');
print('HEAD请求获取到到 isRedirect:${_headReq.isRedirect}\n');
print('HEAD请求获取到到 statusCode:${_headReq.statusCode}\n');
print('HEAD请求获取到到 statusMessage:${_headReq.statusMessage}\n');

POST 请求

向指定资源提交数据 , 请求服务器处理 (例如提交表单或者上传文件) 。数据被包含在请求体中 , 数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源 。表单数据被浏览器编码到body里然后发送请求 。

body主要有四种格式 :

application/x-www-form-urlencode (传递简单数据 / 格式 : 是"key1=value1&key2=value2") ,对于二进制文件这种数据的传输效率很低  。默认传递数据的格式 。消息包大,耗流量  。

 try {Response _headReq = await Dio().post(///请求地址'https://tieba.baidu.com/f',///请求参数data: {'ie': 'utf-8', 'kw': '大佬', 'fr': 'search'},///请求头options: new Options(contentType: Headers.formUrlEncodedContentType,),);print('HEAD请求获取到到 数据:${_headReq.data}\n');print('HEAD请求获取到到 extra:${_headReq.extra}\n');print('HEAD请求获取到到 headers:${_headReq.headers}\n');print('HEAD请求获取到到 isRedirect:${_headReq.isRedirect}\n');print('HEAD请求获取到到 statusCode:${_headReq.statusCode}\n');print('HEAD请求获取到到 statusMessage:${_headReq.statusMessage}\n');} catch (e) {print('请求异常:' + e.toString());}

multipart/form-data

        multipart/form-data 定义在 rfc2388 中 , 用以支持向服务器发送二进制数据。这种编码方式,通常是用在客户端向服务端传送大文件数据,如:图片或者文件。

boundary 是一个占位符,代表我们规定的具体分割符;可以自己任意规定,但为了避免和正常文本重复了。

Boundary 参数设置注意事项:

必须以英文中间双横杠--开头,这个--称为前导连字符

除了前导连字符以外的部分不能超过70个字符

不能包含HTTP协议或者URL禁用的特殊意义的字符,例如英文冒号(:)等

 FormData _formData = new FormData.fromMap({'file': await MultipartFile.fromFile(///手机存储卡上图片路径'filePath',///图片名称filename: 'fileName',),});Response _headReq = await Dio().post('url',data: _formData,options: Options(method: 'POST', contentType: 'multipart/form-data;boundary=xxxx'));print('HEAD请求获取到到 数据:${_headReq.data}\n');

application/json

客户端向服务端传递序列化的JSON字符串。方便的提交复杂的结构化数据,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自带的开发者工具、Firebug、Fiddler,都会以树形结构展示 JSON 数据,非常友好。

 Response _headReq = await Dio().post('https://www.wanandroid.com',data: {'username': '', 'password': '',},options: Options(contentType: 'application/json',),);print('HEAD请求获取到到 数据:${_headReq.data}\n');

text/xml

传输和存储数据,它非常适合万维网传输。以纯文本形式进行编码,其中不包含任何控件或格式字符, 大部分情况不会使用 。

PUT 请求

创建或者替换目标资源 。put调用一次和多次是等价的。而连续调用多次POST方法可能会有副作用,比如将一个订单重复提交多次。

DELETE 请求

向服务端请求删除某个已存在的资源 。

CONNECT

        HTTP 协议中,CONNECT 方法可以开启一个客户端与所请求资源之间的双向沟通的通道 。

connect是为了建立http tunnel , 只有在受限制的网络环境中(防火墙、NAT、代理器)并且是https通信时,客户端使用http connect请求代理服务器,代理服务器使用connect方法与目标服务器建立http tunnel,通道建立后,客户端与服务器进行通信,代理服务器就像透明一样,只是接收、转发tcp stream。

建立http tunnel 的理由 ?
这是因为,网络环境受限,客户端无法直接访问某些网络,所以只能通过代理服务器访问网络,然后,将内容转发给客户端,从宏观上看,客户端与服务器端就像建立了一条隧道一样。
但是由于http tunnnel可控性不强,所以,服务器通常会限制"可connect的端口"(一般只开放SSL的443端口)

HTTPS (HTTP over SSL/TLS) 是 HTTP 的安全版本,在 HTTP 上加了一层处理加密信息的模块。SSL/TLS 全称安全传输层协议 Transport Layer Security, 是介于 TCP 和 HTTP 之间的一层安全协议,不影响原有的 TCP 协议和 HTTP 协议,所以使用HTTPS基本上不需要对 HTTP 页面进

行太多的改造。浏览器访问支持 HTTPS 的站点时,在地址栏的前面会有一把绿色的锁一样的标

识,表明 HTTPS 生效了。

HTTPS的主要作用是

对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全

对网站服务器进行真实身份认证

SSL/TLS 协议采用非对称加密方式,服务端会生成公钥和私钥,公钥用来加密信息,可以提供给所有需要进行通信的客户端,私钥保存在本地,不能泄露。客户端使用这份公钥对信息进行加密,将请求发送给服务器,服务器用私钥解密。反之,服务器对客户端的返回,则使用客户端提供的公钥进行加密,客户端使用本地对应的私钥来解密,保证了通信的安全。

基于 SSL/TLS 进行 一次的 HTTPS 会话的过程,简单地说可以分成3步

客户端向服务器端索要并验证公钥。

双方协商生成”对话密钥”。

双方采用”对话密钥”进行加密通信。

HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,使用 HTTPS 则有以下几个方面的优势:

保护站点安全、保护用户隐私、未来的趋势所在

升级HTTPS :

获取证书、在服务器安装证书、重定向配置、修改资源链接

/ 网络请求神器DIO /

pubspec.yaml 文件配置dio插件依赖

dio版本号建议配置成any , 以后即便更新了flutter sdk 也不用手动去配置版本号

查看flutter sdk、dart sdk 版本号

网络请求创建 单利模式

确保某一个类只有一个实例,而且自行实例化并向整个应用提供这个实例。

适用于

全局日志的 Logger 类、应用全局的配置数据对象类,单业务管理类。
创建实例时占用资源较多,或实例化耗时较长的类。
等等…

class HttpManager {///存储网络请求的token,方便网络请求完成后取消网络请求Map<String, CancelToken> _cancelTokens = Map<String, CancelToken>();///超时时间static const int CONNECT_TIMEOUT = 30000;static const int RECEIVE_TIMEOUT = 30000;Dio? _client;static final HttpManager _instance = HttpManager._internal();factory HttpManager() => _instance;Dio get client => _client!;/// 创建 dio 实例对象HttpManager._internal() {if (_client == null) {/// 全局属性:请求前缀、连接超时时间、响应超时时间BaseOptions options = BaseOptions(connectTimeout: CONNECT_TIMEOUT,receiveTimeout: RECEIVE_TIMEOUT,);_client = Dio(options);}}
}

 创建网络请求HttpError

        通过网络请求状态码来提示用户 (网络错误、解析错误、证书错误、协议错误、响应超时、发送超时、网络请求错误 等...)

/// 网络请求错误
class HttpError {///HTTP 状态码static const int UNAUTHORIZED = 401;static const int FORBIDDEN = 403;static const int NOT_FOUND = 404;static const int REQUEST_TIMEOUT = 408;static const int INTERNAL_SERVER_ERROR = 500;static const int BAD_GATEWAY = 502;static const int SERVICE_UNAVAILABLE = 503;static const int GATEWAY_TIMEOUT = 504;///未知错误static const String UNKNOWN = "UNKNOWN";///解析错误static const String PARSE_ERROR = "PARSE_ERROR";///网络错误static const String NETWORK_ERROR = "NETWORK_ERROR";///协议错误static const String HTTP_ERROR = "HTTP_ERROR";///证书错误static const String SSL_ERROR = "SSL_ERROR";///连接超时static const String CONNECT_TIMEOUT = "CONNECT_TIMEOUT";///响应超时static const String RECEIVE_TIMEOUT = "RECEIVE_TIMEOUT";///发送超时static const String SEND_TIMEOUT = "SEND_TIMEOUT";///网络请求取消static const String CANCEL = "CANCEL";String? code;String? message;HttpError(this.code, this.message);HttpError.dioError(DioError error) {message = error.message;switch (error.type) {case DioErrorType.connectTimeout:code = CONNECT_TIMEOUT;message = "网络连接超时,请检查网络设置";break;case DioErrorType.receiveTimeout:code = RECEIVE_TIMEOUT;message = "服务器异常,请稍后重试!";break;case DioErrorType.sendTimeout:code = SEND_TIMEOUT;message = "网络连接超时,请检查网络设置";break;case DioErrorType.response:code = HTTP_ERROR;message = "服务器异常,请稍后重试!";break;case DioErrorType.cancel:code = CANCEL;message = "请求已被取消,请重新请求";break;case DioErrorType.other:code = UNKNOWN;message = "未知错误,请稍后重试!";break;}}@overrideString toString() {return 'HttpError{code: $code, message: $message}';}
}

main()函数初始化 网络请求公共参数

 ///初始化公共属性////// [baseUrl] 地址前缀/// [connectTimeout] 连接超时赶时间/// [receiveTimeout] 接收超时赶时间/// [interceptors] 基础拦截器void init({String? baseUrl,int? connectTimeout,int? receiveTimeout,List<Interceptor>? interceptors}) {_client?.options = _client!.options.copyWith(baseUrl: baseUrl,connectTimeout: connectTimeout,receiveTimeout: receiveTimeout,);if (interceptors != null && interceptors.isNotEmpty) {_client!.interceptors..addAll(interceptors);}}
void main() async{WidgetsFlutterBinding.ensureInitialized();await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);await SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);///网络请求管理初始化HttpManager().init(baseUrl: '网络请求域名前缀');runApp(MyApp());
}

创建POST和GET统一网络请求

///统一网络请求///[url] 网络请求地址不包含域名///[data] post 请求参数///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求void _request({String? url,String? method,data,Map<String, dynamic>? params,Options? options,HttpSuccessCallback? successCallback,HttpFailureCallback? errorCallback,@required String? tag,}) async {//检查网络是否连接ConnectivityResult connectivityResult =await (Connectivity().checkConnectivity());if (connectivityResult == ConnectivityResult.none) {if (errorCallback != null) {errorCallback(HttpError(HttpError.NETWORK_ERROR, "网络异常,请稍后重试!"));}return;}//设置默认值params = params ?? {};method = method ?? 'GET';options?.method = method;options = options ?? Options(method: method,);///请求头options.headers = await _headers();try {CancelToken cancelToken;cancelToken =(_cancelTokens[tag] == null ? CancelToken() : _cancelTokens[tag])!;_cancelTokens[tag!] = cancelToken;Response response = await _client!.request(url!,data: data,queryParameters: params,options: options,cancelToken: cancelToken);var _responseData = response.data;print('响应的数据:$_responseData');int statusCode = _responseData["code"];if (statusCode == 200) {//成功successCallback!(_responseData["data"]);} else {//失败String message = _responseData["msg"].toString();errorCallback!(HttpError('$statusCode', message));}} on DioError catch (e, s) {if (e.type != DioErrorType.cancel) {errorCallback!(HttpError.dioError(e));}} catch (e, s) {errorCallback!(HttpError(HttpError.UNKNOWN, "未知错误,请稍后重试!"));}}

取消网络请求、自定义header

 ///取消网络请求///取消网络请求void cancel(String tag) {print('取消网络请求前 cancelToken集合$_cancelTokens');if (_cancelTokens.containsKey(tag)) {if (!_cancelTokens[tag]!.isCancelled) {_cancelTokens[tag]!.cancel();}_cancelTokens.remove(tag);print('取消网络请求后 cancelToken集合$_cancelTokens');}}///请求头Future<Map<String, String>> _headers() async {Map<String, String> _headers = new HashMap();String _token = '';_headers.addAll({"token": _token});return _headers;}

GET 请求 

 ///Get网络请求//////[url] 网络请求地址不包含域名///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求get({required String url,required Map<String, dynamic> params,Options? options,required HttpSuccessCallback successCallback,required HttpFailureCallback errorCallback,required String tag,}) async {return _request(url: url,params: params,method: 'get',options: options,successCallback: successCallback,errorCallback: errorCallback,tag: tag,);}

发起GET请求

class _MyHomePageState extends State<MyHomePage> {var _tag;void _testDioHeadReq() async {_tag = '${{DateTime.now().millisecondsSinceEpoch}}';HttpManager().get(url: '',params: {},successCallback: (_data) {print('响应数据:$_data');///取消请求HttpManager().cancel(_tag);},errorCallback: (_data) {print('响应数据错误:$_data');},///请求tag可以用时间戳进行定义tag: '$_tag');}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),floatingActionButton: FloatingActionButton(onPressed: _testDioHeadReq,tooltip: 'TestDioHeadReq',child: Icon(Icons.add),),);}
}

请求结果 :

POST 请求

///post网络请求///[url] 网络请求地址不包含域名///[data] post 请求参数///[params] url请求参数支持restful///[options] 请求配置///[successCallback] 请求成功回调///[errorCallback] 请求失败回调///[tag] 请求统一标识,用于取消网络请求void post({String? url,data,Map<String, dynamic>? params,Options? options,HttpSuccessCallback? successCallback,HttpFailureCallback? errorCallback,@required String? tag,}) async {_request(url: url!,data: data,method: POST,params: params!,options: options!,successCallback: successCallback!,errorCallback: errorCallback!,tag: tag!,);}

修改main()函数初始化 网络请求域名前缀 (BaseUrl)

void main() async {WidgetsFlutterBinding.ensureInitialized();await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);await SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);///网络请求管理初始化HttpManager().init(baseUrl: 'https://tieba.baidu.com/f');runApp(MyApp());
}
var _tag;void _testDioHeadReq() async {_tag = '${{DateTime.now().millisecondsSinceEpoch}}';HttpManager().post(url: '',params: {'ie':'utf-8','kw':'大佬','fr':'search'},successCallback: (_data) {print('响应数据:$_data');///取消请求HttpManager().cancel(_tag);},errorCallback: (_data) {print('响应数据错误:$_data');},///请求tag可以用时间戳进行定义tag: '$_tag');}

请求结果 :

flutter_test_dio案例下载

 后续

接下来的日子,我会在博客里面演练MVC、MVP、MVVM与Dio结合进行网络请求

参考HTTP状态码

Flutter实战MVC、MVP、MVVM

Flutter 项目实战 Dio网络请求 四相关推荐

  1. flutter 项目实战二 网络请求

    本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用. 逛丢官方网址:https://guangdiu.com/ flutter windows开发环境设置 flutter 项目实战 ...

  2. Flutter 项目实战(Dio+MVP+FutureBuilder )五

    / 没有感情万千 .只有默默无闻 / 2022年跨年了,又涨了一岁.随着时光的流逝,工作多年的我还是在坚持些代码.互联网都有所谓的大龄危机,我对此毫无畏惧.不要因为社会存在一些大龄危机的恐慌,产生了很 ...

  3. Flutter 项目实战 自定义选择器 十四

  4. flutter 项目实战四 列表数据展示

    本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用. 逛丢官方网址:https://guangdiu.com/ flutter windows开发环境设置 flutter 项目实战 ...

  5. 12、Flutter - 项目实战 - 仿微信(六)聊天页面

    Flutter - 项目实战 - 仿微信(六)聊天页面 接上篇:11.Flutter - 项目实战 - 仿微信(五)通讯录 详细代码参见Demo Demo地址 -> wechat_demo 其他 ...

  6. Flutter实战之网络请求框架Dio入门使用

    本篇博文涉及到的demo很简单,就是通过调用天气查询接口来显示城市的天气信息.通过本demo可以了解: 1.CityPicker的简单使用 2.Dio网络请求库的简单使用 3.Flutter对json ...

  7. Flutter开发之HTTP网络请求:dio库(28)

    dio是Flutter中文网开源的一个强大的Dart Http请求库,支持Restful API.FormData.拦截器.请求取消.Cookie管理.文件上传/下载.超时等- 第三方库 dio实现g ...

  8. Flutter 实战开发-网络请求

    flutter中常见的网络请求有三种分别是 1,Dart 原生的网络请求 HttpClient. 2,第三方网络请求 http 3,以及 Flutter 中的 Dio. 本文主要比较细致的讲解上述网络 ...

  9. 笨办法学Python(第四版)最新版+Python爬虫开发与项目实战+Python网络数据采集+精通Scrapy网络爬虫

    笨办法学Python(第四版)最新版+Python爬虫开发与项目实战+Python网络数据采集+精通Scrapy网络爬虫 本资料为最新整理高清带目录pdf,百度网盘下载~~~ 本资料为最新整理高清带目 ...

最新文章

  1. UITableView数据更新问题
  2. 神经网络 | Mask Scoring R-CNN:实例分割综述
  3. CRMEB后台前端文档说明
  4. 创建xmlhttp对象
  5. 开发指南专题十八:Navicat 数据库转换操作
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的实验室设备管理系统
  7. python源文件后缀_Python怎样获取文件扩展名
  8. springboot解决第三方依赖jar包的问题
  9. UnityShader19.1:渲染纹理(下)之GrabPass
  10. 如何使用手机端、ipad端来编写博客
  11. request与在php安全,request导致的安全性问题分析
  12. 【Xamarin开发 Android 系列 6】 Android 结构基础(上)
  13. python添加音乐_python给视频添加背景音乐并改变音量的具体方法
  14. 有赞云支付php接口,Erphpdown wordpress插件集成有赞云支付的接口申请方法[已失效]...
  15. js树结构数据的递归操作
  16. css的外链写法,纯CSS代码为外链增加图标
  17. 游戏开发之Unity2021熟悉基本工具
  18. 按照拼音排序的SQL语句条件
  19. 基于Matlab雷达探测系统(GUI界面模拟)
  20. E-R图与数据库模型学习心得

热门文章

  1. 程序员也需要这种魄力
  2. 网络基础UDP实例(传一个long类型的值)
  3. 用python分析小说_用Python分析《斗破苍穹》
  4. mysql 输出名称_MySQL常用的SQL语句//输出所有信息showfullfieldsfrom'表名称';//改表
  5. android北京工资待遇,【北京京东工资】android开发工程师待遇-看准网
  6. python类的简单定义
  7. 深入浅出 “三门问题”
  8. beego 跨域问题
  9. springboot大学生社团管理系统的设计与实现毕业设计源码150912
  10. Linux启动过程以及引导错误修复