flutter dio 示例
GIT HUB常常打不开,保存一份以方便随时查看。
示例
发起一个 GET
请求 :
Response response;
var dio = Dio();
response = await dio.get('/test?id=12&name=wendu');
print(response.data.toString());
// Optionally the request above could also be done as
response = await dio.get('/test', queryParameters: {'id': 12, 'name': 'wendu'});
print(response.data.toString());
发起一个 POST
请求:
response = await dio.post('/test', data: {'id': 12, 'name': 'wendu'});
发起多个并发请求:
response = await Future.wait([dio.post('/info'), dio.get('/token')]);
下载文件:
response = await dio.download('https://www.google.com/', './xx.html');
以流的方式接收响应数据:
Response<ResponseBody> rs;
rs = await Dio().get<ResponseBody>(url,options: Options(responseType: ResponseType.stream), //设置接收类型为stream
);
print(rs.data.stream); //响应流
以二进制数组的方式接收响应数据:
Response<List<int>> rs
rs = await Dio().get<List<int>>(url,options: Options(responseType: ResponseType.bytes), //设置接收类型为二进制数组
);
print(rs.data); // 二进制数组
发送 FormData:
var formData = FormData.fromMap({'name': 'wendux','age': 25,
});
var response = await dio.post('/info', data: formData);
通过FormData上传多个文件:
var formData = FormData.fromMap({'name': 'wendux','age': 25,'file': await MultipartFile.fromFile('./text.txt', filename: 'upload.txt'),'files': [await MultipartFile.fromFile('./text1.txt', filename: 'text1.txt'),await MultipartFile.fromFile('./text2.txt', filename: 'text2.txt'),]
});
var response = await dio.post('/info', data: formData);
监听发送(上传)数据进度:
response = await dio.post('http://www.dtworkroom.com/doris/1/2.0.0/test',data: {'aa': 'bb' * 22},onSendProgress: (int sent, int total) {print('$sent $total');},
);
以流的形式提交二进制数据:
List<int> postData = <int>[...];
await dio.post(url,data: Stream.fromIterable(postData.map((e) => [e])), //创建一个Stream<List<int>>options: Options(headers: {Headers.contentLengthHeader: postData.length, // 设置content-length},),
);// 二进制数据
List<int> postData = <int>[...];await dio.post(url,data: Stream.fromIterable(postData.map((e) => [e])), //创建一个Stream<List<int>>options: Options(headers: {Headers.contentLengthHeader: postData.length, // 设置content-length},),
);
注意:如果要监听提交进度,则必须设置content-length,否则是可选的。
示例目录
你可以在这里查看dio的全部示例.
Dio APIs
创建一个Dio实例,并配置它
建议在项目中使用Dio单例,这样便可对同一个dio实例发起的所有请求进行一些统一的配置,比如设置公共header、请求基地址、超时时间等;这里有一个在Flutter工程中使用Dio单例(定义为top level变量)的示例供开发者参考。
你可以使用默认配置或传递一个可选 BaseOptions
参数来创建一个Dio实例 :
var dio = Dio(); // with default Options// Set default configs
dio.options.baseUrl = 'https://www.xx.com/api';
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;// or new Dio with a BaseOptions instance.
var options = BaseOptions(baseUrl: 'https://www.xx.com/api',connectTimeout: 5000,receiveTimeout: 3000,
);
Dio dio = Dio(options);
Dio实例的核心API是 :
Future request(String path, {data,Map queryParameters, Options options,CancelToken cancelToken, ProgressCallback onSendProgress, ProgressCallback onReceiveProgress)
response = await dio.request('/test',data: {'id':12,'name':'xx'},options: Options(method:'GET'),
);
请求方法别名
为了方便使用,Dio提供了一些其它的Restful API, 这些API都是request
的别名。
Future get(...)
Future post(...)
Future put(...)
Future delete(...)
Future head(...)
Future put(...)
Future path(...)
Future download(...)
Future fetch(RequestOptions) new*
请求配置
BaseOptions
描述的是Dio实例发起网络请求的的公共配置,而Options
类描述了每一个Http请求的配置信息,每一次请求都可以单独配置,单次请求的Options
中的配置信息可以覆盖BaseOptions
中的配置,下面是BaseOptions
的配置项:
{/// Http method.String method;/// 请求基地址,可以包含子路径,如: "https://www.google.com/api/".String baseUrl;/// Http请求头.Map<String, dynamic> headers;/// 连接服务器超时时间,单位是毫秒.int connectTimeout;/// 2.x中为接收数据的最长时限.int receiveTimeout;/// 请求路径,如果 `path` 以 "http(s)"开始, 则 `baseURL` 会被忽略; 否则,/// 将会和baseUrl拼接出完整的的url.String path = "";/// 请求的Content-Type,默认值是"application/json; charset=utf-8"./// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,/// 可以设置此选项为 `Headers.formUrlEncodedContentType`, 这样[Dio]/// 就会自动编码请求体.String contentType;/// [responseType] 表示期望以那种格式(方式)接受响应数据。/// 目前 [ResponseType] 接受三种类型 `JSON`, `STREAM`, `PLAIN`.////// 默认值是 `JSON`, 当响应头中content-type为"application/json"时,dio 会自动将响应内容转化为json对象。/// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `STREAM`.////// 如果想以文本(字符串)格式接收响应数据,请使用 `PLAIN`.ResponseType responseType;/// `validateStatus` 决定http响应状态码是否被dio视为请求成功, 返回`validateStatus`/// 返回`true` , 请求结果就会按成功处理,否则会按失败处理.ValidateStatus validateStatus;/// 用户自定义字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中取到.Map<String, dynamic> extra;/// Common query parametersMap<String, dynamic /*String|Iterable<String>*/ > queryParameters;/// 请求数据中数组的编码的方式,具体可以参考CollectionFormat的定义late CollectionFormat collectionFormat;
}
这里有一个完成的示例.
响应数据
当请求成功时会返回一个Response对象,它包含如下字段:
{/// 响应数据,可能已经被转换了类型, 详情请参考Options中的[ResponseType].T data;/// 响应头Headers headers;/// 本次请求信息Options request;/// Http status code.int? statusCode;String? statusMessage;/// 是否重定向(Flutter Web不可用)bool? isRedirect;/// 重定向信息(Flutter Web不可用)List<RedirectInfo> redirects ;/// 真正请求的url(重定向最终的uri)Uri realUri;/// 响应对象的自定义字段(可以在拦截器中设置它),调用方可以在`then`中获取.Map<String, dynamic> extra;
}
示例如下:
Response response = await dio.get('https://www.google.com');
print(response.data);
print(response.headers);
print(response.request);
print(response.statusCode);
拦截器
每个 Dio 实例都可以添加任意多个拦截器,他们组成一个队列,拦截器队列的执行顺序是FIFO。通过拦截器你可以在请求之前、响应之后和发生异常时(但还没有被 then
或 catchError
处理)做一些统一的预处理操作。
dio.interceptors.add(InterceptorsWrapper(onRequest:(options, handler){// Do something before request is sentreturn handler.next(options); //continue// 如果你想完成请求并返回一些自定义数据,你可以resolve一个Response对象 `handler.resolve(response)`。// 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response.//// 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象,如`handler.reject(error)`,// 这样请求将被中止并触发异常,上层catchError会被调用。},onResponse:(response,handler) {// Do something with response datareturn handler.next(response); // continue// 如果你想终止请求并触发一个错误,你可以 reject 一个`DioError`对象,如`handler.reject(error)`,// 这样请求将被中止并触发异常,上层catchError会被调用。},onError: (DioError e, handler) {// Do something with response errorreturn handler.next(e);//continue// 如果你想完成请求并返回一些自定义数据,可以resolve 一个`Response`,如`handler.resolve(response)`。// 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response.}
));
一个简单的自定义拦截器示例:
import 'package:dio/dio.dart';
class CustomInterceptors extends Interceptor {@overridevoid onRequest(RequestOptions options, RequestInterceptorHandler handler) {print('REQUEST[${options.method}] => PATH: ${options.path}');return super.onRequest(options, handler);}@overrideFuture onResponse(Response response, ResponseInterceptorHandler handler) {print('RESPONSE[${response.statusCode}] => PATH: ${response.request?.path}');return super.onResponse(response, handler);}@overrideFuture onError(DioError err, ErrorInterceptorHandler handler) {print('ERROR[${err.response?.statusCode}] => PATH: ${err.request.path}');return super.onError(err, handler);}
}
完成和终止请求/响应
在所有拦截器中,你都可以改变请求执行流, 如果你想完成请求/响应并返回自定义数据,你可以resolve一个 Response
对象或返回 handler.resolve(data)
的结果。 如果你想终止(触发一个错误,上层catchError
会被调用)一个请求/响应,那么可以reject一个DioError
对象或返回 handler.reject(errMsg)
的结果.
dio.interceptors.add(InterceptorsWrapper(onRequest:(options, handler) {return handler.resolve(Response(requestOptions:options,data:'fake data'));},
));
Response response = await dio.get('/test');
print(response.data);//'fake data'
Lock/unlock 拦截器
你可以通过调用拦截器的 lock()
/unlock
方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。
tokenDio = Dio(); //Create a new instance to request the token.
tokenDio.options = dio.options.copyWith();
dio.interceptors.add(InterceptorsWrapper(onRequest:(Options options, handler){// If no token, request token firstly and lock this interceptor// to prevent other request enter this interceptor.dio.interceptors.requestLock.lock();// We use a new Dio(to avoid dead lock) instance to request token.tokenDio.get('/token').then((response){//Set the token to headersoptions.headers['token'] = response.data['data']['token'];handler.next(options); //continue}).catchError((error, stackTrace) {handler.reject(error, true);}).whenComplete(() => dio.interceptors.requestLock.unlock());}
));
Clear()
你也可以调用拦截器的clear()
方法来清空等待队列。
别名
当请求拦截器被锁定时,接下来的请求将会暂停,这等价于锁住了dio实例,因此,Dio示例上提供了请求拦截器lock/unlock
的别名方法:
dio.lock() == dio.interceptors.requestLock.lock()
dio.unlock() == dio.interceptors.requestLock.unlock()
dio.clear() == dio.interceptors.requestLock.clear()
示例
假设这么一个场景:出于安全原因,我们需要给所有的请求头中添加一个csrfToken,如果csrfToken不存在,我们先去请求csrfToken,获取到csrfToken后,再发起后续请求。 由于请求csrfToken的过程是异步的,我们需要在请求过程中锁定后续请求(因为它们需要csrfToken), 直到csrfToken请求成功后,再解锁,代码如下:
dio.interceptors.add(InterceptorsWrapper(onRequest: (Options options, handler) async {print('send request:path:${options.path},baseURL:${options.baseUrl}');if (csrfToken == null) {print('no token,request token firstly...');//lock the dio.dio.lock();tokenDio.get('/token').then((d) {options.headers['csrfToken'] = csrfToken = d.data['data']['token'];print('request token succeed, value: ' + d.data['data']['token']);print( 'continue to perform request:path:${options.path},baseURL:${options.path}');handler.next(options);}).catchError((error, stackTrace) {handler.reject(error, true);}) .whenComplete(() => dio.unlock()); // unlock the dio} else {options.headers['csrfToken'] = csrfToken;handler.next(options);}}
));
完整的示例代码请点击 这里.
日志
我们可以添加 LogInterceptor
拦截器来自动打印请求、响应日志, 如:
dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
由于拦截器队列的执行顺序是FIFO,如果把log拦截器添加到了最前面,则后面拦截器对
options
的更改就不会被打印(但依然会生效), 所以建议把log拦截添加到队尾。
Cookie管理
dio_cookie_manager 包是Dio的一个插件,它提供了一个Cookie管理器。详细示例可以移步dio_cookie_manager 。
自定义拦截器
开发者可以通过继承Interceptor
类来实现自定义拦截器,这是一个简单的缓存示例拦截器。
错误处理
当请求过程中发生错误时, Dio 会包装 Error/Exception
为一个 DioError
:
try {//404await dio.get('https://wendux.github.io/xsddddd');
} on DioError catch (e) {// The request was made and the server responded with a status code// that falls out of the range of 2xx and is also not 304.if (e.response) {print(e.response.data)print(e.response.headers)print(e.response.request)} else {// Something happened in setting up or sending the request that triggered an Errorprint(e.request)print(e.message)}
}
DioError 字段
{/// Request info.RequestOptions request;/// Response info, it may be `null` if the request can't reach to/// the http server, for example, occurring a dns error, network is not available.Response response;/// 错误类型,见下文DioErrorType type;///原始的error或exception对象,通常type为DEFAULT时存在。dynamic error;
}
DioErrorType
enum DioErrorType {/// It occurs when url is opened timeout.connectTimeout,/// It occurs when url is sent timeout.sendTimeout,///It occurs when receiving timeout.receiveTimeout,/// When the server response, but with a incorrect status, such as 404, 503...response,/// When the request is cancelled, dio will throw a error with this type.cancel,/// Default error type, Some other Error. In this case, you can/// use the DioError.error if it is not null.other,
}
使用application/x-www-form-urlencoded编码
默认情况下, Dio 会将请求数据(除过String类型)序列化为 JSON
. 如果想要以 application/x-www-form-urlencoded
格式编码, 你可以显式设置contentType
:
//Instance level
dio.options.contentType= Headers.formUrlEncodedContentType;
//or works once
dio.post('/info',data: {'id': 5},options: Options(contentType: Headers.formUrlEncodedContentType),
);
这里有一个示例.
FormData
Dio支持发送 FormData, 请求数据将会以 multipart/form-data
方式编码, FormData中可以一个或多个包含文件 .
var formData = FormData.fromMap({'name': 'wendux','age': 25,'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt')
});
response = await dio.post('/info', data: formData);
注意: 只有 post 方法支持发送 FormData.
这里有一个完整的示例.
多文件上传
多文件上传时,通过给key加中括号“[]”方式作为文件数组的标记,大多数后台也会通过key[]这种方式来读取。不过RFC中并没有规定多文件上传就必须得加“[]”,所以有时不带“[]”也是可以的,关键在于后台和客户端得一致。v3.0.0 以后通过Formdata.fromMap()
创建的Formdata
,如果有文件数组,是默认会给key加上“[]”的,比如:
FormData.fromMap({'files': [MultipartFile.fromFileSync('./example/upload.txt', filename: 'upload.txt'),MultipartFile.fromFileSync('./example/upload.txt', filename: 'upload.txt'),]
});
最终编码时会key会为 "files[]",如果不想添加“[]”,可以通过Formdata
的API来构建:
var formData = FormData();
formData.files.addAll([MapEntry('files',MultipartFile.fromFileSync('./example/upload.txt',filename: 'upload.txt'),),MapEntry('files',MultipartFile.fromFileSync('./example/upload.txt',filename: 'upload.txt'),),
]);
这样构建的FormData
的key是不会有“[]”。
转换器
转换器Transformer
用于对请求数据和响应数据进行编解码处理。Dio实现了一个默认转换器DefaultTransformer
作为默认的 Transformer
. 如果你想对请求/响应数据进行自定义编解码处理,可以提供自定义转换器,通过 dio.transformer
设置。
请求转换器
Transformer.transformRequest(...)
只会被用于 'PUT'、 'POST'、 'PATCH'方法,因为只有这些方法才可以携带请求体(request body)。但是响应转换器Transformer.transformResponse()
会被用于所有请求方法的返回数据。
Flutter中设置
如果你在开发Flutter应用,强烈建议json的解码通过compute方法在后台进行,这样可以避免在解析复杂json时导致的UI卡顿。
注意,根据笔者实际测试,发现通过
compute
在后台解码json耗时比直接解码慢很多,建议开发者仔细评估。
// 必须是顶层函数
_parseAndDecode(String response) {return jsonDecode(response);
}parseJson(String text) {return compute(_parseAndDecode, text);
}void main() {...// 自定义 jsonDecodeCallback(dio.transformer as DefaultTransformer).jsonDecodeCallback = parseJson;runApp(MyApp());
}
其它示例
这里有一个 自定义Transformer的示例.
执行流
虽然在拦截器中也可以对数据进行预处理,但是转换器主要职责是对请求/响应数据进行编解码,之所以将转化器单独分离,一是为了和拦截器解耦,二是为了不修改原始请求数据(如果你在拦截器中修改请求数据(options.data),会覆盖原始请求数据,而在某些时候您可能需要原始请求数据). Dio的请求流是:
请求拦截器 >> 请求转换器 >> 发起请求 >> 响应转换器 >> 响应拦截器 >> 最终结果。
这是一个自定义转换器的示例.
HttpClientAdapter
HttpClientAdapter是 Dio 和 HttpClient之间的桥梁。2.0抽象出adapter主要是方便切换、定制底层网络库。Dio实现了一套标准的、强大API,而HttpClient则是真正发起Http请求的对象。我们通过HttpClientAdapter将Dio和HttpClient解耦,这样一来便可以自由定制Http请求的底层实现,比如,在Flutter中我们可以通过自定义HttpClientAdapter将Http请求转发到Native中,然后再由Native统一发起请求。再比如,假如有一天OKHttp提供了dart版,你想使用OKHttp发起http请求,那么你便可以通过适配器来无缝切换到OKHttp,而不用改之前的代码。
Dio 使用DefaultHttpClientAdapter
作为其默认HttpClientAdapter,DefaultHttpClientAdapter
使用dart:io:HttpClient
来发起网络请求。
设置Http代理
DefaultHttpClientAdapter
提供了一个onHttpClientCreate
回调来设置底层 HttpClient
的代理,我们想使用代理,可以参考下面代码:
import 'package:dio/dio.dart';
import 'package:dio/adapter.dart';
...
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {// config the http clientclient.findProxy = (uri) {//proxy all request to localhost:8888return 'PROXY localhost:8888';};// you can also create a HttpClient to dio// return HttpClient();
};
完整的示例请查看这里.
Https证书校验
有两种方法可以校验https证书,假设我们的后台服务使用的是自签名证书,证书格式是PEM格式,我们将证书的内容保存在本地字符串中,那么我们的校验逻辑如下:
String PEM='XXXXX'; // certificate content
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {client.badCertificateCallback=(X509Certificate cert, String host, int port){if(cert.pem==PEM){ // Verify the certificatereturn true;}return false;};
};
X509Certificate
是证书的标准格式,包含了证书除私钥外所有信息,读者可以自行查阅文档。另外,上面的示例没有校验host,是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了(而不是中间人),host验证通常是为了防止证书和域名不匹配。
对于自签名的证书,我们也可以将其添加到本地证书信任链中,这样证书验证时就会自动通过,而不会再走到badCertificateCallback
回调中:
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {SecurityContext sc = SecurityContext();//file is the path of certificatesc.setTrustedCertificates(file);HttpClient httpClient = HttpClient(context: sc);return httpClient;
};
注意,通过setTrustedCertificates()
设置的证书格式必须为PEM或PKCS12,如果证书格式为PKCS12,则需将证书密码传入,这样则会在代码中暴露证书密码,所以客户端证书校验不建议使用PKCS12格式的证书。
Http2支持
dio_http2_adapter 包提供了一个支持Http/2.0的Adapter,详情可以移步 dio_http2_adapter 。
请求取消
你可以通过 cancel token 来取消发起的请求:
CancelToken token = CancelToken();
dio.get(url, cancelToken: token).catchError((DioError err){if (CancelToken.isCancel(err)) {print('Request canceled! '+ err.message)}else{// handle error.}});
// cancel the requests with "cancelled" message.
token.cancel("cancelled");
注意: 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
完整的示例请参考取消示例.
继承 Dio class
Dio
是一个拥有factory 构造函数的接口类,因此不能直接继承 Dio
,但是可以通过 DioForNative
或DioForBrowser
来间接实现:
import 'package:dio/dio.dart';
import 'package:dio/native_imp.dart'; //在浏览器中, import 'package:dio/browser_imp.dart'class Http extends DioForNative {Http([BaseOptions options]):super(options){// 构造函数做一些事}
}
我们也可以直接实现 Dio
接口类 :
class MyDio with DioMixin implements Dio{// ...
}
flutter dio 示例相关推荐
- 【Flutter】Flutter 拍照示例 ( 拍照并获取照片源码示例 | image_picker 0.7.2+1 | Flutter 最新 SDK 安装 )
文章目录 一.image_picker 使用 二.更新 Flutter SDK 三.image_picker 使用示例 四.相关资源 之前在 [Flutter]Flutter 拍照示例 ( 拍照并获取 ...
- 【Flutter】Flutter 拍照示例 ( Flutter 插件配置 | Flutter 插件源码示例 | iOS 应用配置 | Android 应用配置 )
文章目录 一.Flutter 插件配置 二.Flutter 插件源码示例 三.iOS 应用配置 四.Android 应用配置 五.相关资源 一.Flutter 插件配置 Flutter 拍照示例中 , ...
- Flutter Dio的简易封装和demo
flutter_net_demo 一个简单的flutter dio的封装, 包括: 日志打印, 网络进度, 返回json转bean, get post upload方法的封装, 简易可直接运行的dem ...
- Flutter Dio 网络接口与请求数据
Flutter Dio 网络接口与请求数据 想了想,感觉没什么好说的,像什么get,post,delete,download啥的,基本插件的使用文档都有,没什么好说的,但是我们实际项目写的时候,可不能 ...
- Flutter - dio 简单二次封装
demo 地址: https://github.com/iotjin/jh_flutter_demo Flutter Dio简单二次封装和自定义Header Flutter Dio二次封装 Flutt ...
- Flutter —— dio
Flutter -- dio 1. 关于import 2. pubspec 3. Dio 4. 替换项目三方库 1. 关于import import 中 as关键字来给他起了个别名来避免类名.方法名冲 ...
- Flutter dio XMLHttpRequest error
Flutter dio XMLHttpRequest error 原文:https://blog.csdn.net/weixin_44259356/article/details/105601933 ...
- flutter 入门示例_AnyChart入门— 10个实用示例
flutter 入门示例 If your website is data-intensive, then you will need to make that data easy to visuali ...
- flutter dio+rxdart
线上flutter 接口请求基本架构(线上实际项目搭建 不多讲直接上代码) 首先添加库: pubspec.yaml 目录下: dio: ^2.1.2 #dio 请求框架rxdart: ^0.21.0 ...
最新文章
- Python Qt GUI设计:QLineEdit和QTextEdit文本框类(基础篇—13)
- 购买IBM System x3650 M4十大理由
- angularjs html5模式,angularjs – 确定在控制器中启用了HTML5模式
- java servletcontext_Java ServletContext对象用法解析
- JavaScript DOM编程艺术 - 读书笔记1-3章
- win7下简单FTP服务器搭建
- 项目管理过程组(5)与知识领域(10)
- 阿里云数据库MYSQL和自建数据库的对比(附新手领取免费的一个月云服务器)
- leetcode738. 单调递增的数字
- linux---基础02
- Java.Lang.NoSuchMethod 错误
- Oracle数据库忘记用户名和密码怎么办
- JAVA对MYSQL进行连接、插入、修改、删除操作
- 点播系统加服务器加投影加音响,VOD视频点播系统的安装维护与使用
- 如何创建强命名程序集(Strong Name Assembly)
- [译]Hierarchical Macro Strategy Model for MOBA Game AI(王者荣耀)--翻译
- ios系统安装包下载_iOS 屏蔽系统升级,描述文件版本已复活,无需越狱,请速度下载!...
- mysql b树_为什么 MongoDB 索引选择B树,而 Mysql 选择B+树(精干总结)
- Android 九宫格图片展示的实现
- java面试分为_【中软国际Java面试】面试分为两轮。-看准网
热门文章
- 程序员读《马云全转》
- 支付宝支付整体流程,包括验签【同步】【异步】
- Vue、VueCLI(脚手架)安装、使用详细步骤
- python查天气预报_Python如何读取天气预报
- 测回角值计算用计算机,用测回法观测水平角,若半测回角值计算时右目标读数小于左目标读数,则应()。...
- 【今日CV 计算机视觉论文速览 92期】 2 Apr 2019
- Gaussian量子化学计算、LAMMPS分子动力学模拟
- python分析股票支撑压力_python+requests接口压力测试500次,查看响应时间的实例
- ransac直线段拟合
- 计算机设备的名称,计算机设备名称词汇