dotnet不是内部或外部命令需要安装 ASP.NET Core Runtime地址
找到对应系统对应版本下载安装
如果系统是windows server2008可能会报缺少补丁的错误,需要安装系统补丁Windows6.1-KB2533623-x64地址
【WebAPI No.3】API的访问控制IdentityServer4 - YanBigFeg - 博客园
来源: 【WebAPI No.3】API的访问控制IdentityServer4 – YanBigFeg – 博客园
介绍:
IdentityServer是一个OpenID Connect提供者 – 它实现了OpenID Connect和OAuth 2.0协议。是一种向客户发放安全令牌的软件。
官网给出的功能解释是:
- 保护您的资源
- 使用本地帐户存储或通过外部身份提供商对用户进行身份验证
- 提供会话管理和单点登录
- 管理和认证客户
- 向客户发布身份和访问令牌
- 验证令牌
IdentityServe4的四种模式:
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
以下是IdentityServer的一个大致流程图:

注册IdentityServe4认证服务器:
先在ASP.NET core我们选中空模版。因为本身写的业务也不多,只是为了做token的认证处理,所有建这个做测试比较方便。

创建代码示例:
什么时候都不要忘记添加引用哦:
NuGet命令行:Install-Package IdentityServer4
当然你也可以这样:

然后创建config.cs类来处理我们的一些业务:
//定义范围
#region 定义要保护的资源(webapi)
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("FirstAPI", "API接口安全测试")
};
}
#endregion
#region 定义可访问客户端
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
//客户端id自定义
ClientId = "YbfTest",
AllowedGrantTypes = GrantTypes.ClientCredentials, ////设置模式,客户端模式
// 加密验证
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
// client可以访问的范围,在GetScopes方法定义的名字。
AllowedScopes = new List<string>
{
"FirstAPI"
}
}
};
}
#endregion
以上就是一个基本的处理类了。然后我们开始在Startup.cs 配置IdentityServer4:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources()) //配置资源
.AddInMemoryClients(Config.GetClients());//配置客户端
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//将IddiTyServer添加到管道中。
app.UseIdentityServer();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
这样就可以启动项目了,确认项目启动完成后,还要确认服务是否开启成功:在地址后增加地址:/.well-known/openid-configuration 例如:

出现以上结果就是启动成功了。
当然你也可以使用postMan测试工具测试:
需要输入
grant_type为客户端授权client_credentials,client_id为Config中配置的ClientIdClient_Secret为Config中配置的Secret
例如:

创建webAPI资源服务器
这个比较简单了,首先创建一个简单的webapi程序即可。
还是老规矩咯iuput,什么时候都不要忘记引用:
通过nuget添加即可:IdentityServer4.AccessTokenValidation

然后点击确定安装就可以了。
主要认证注册服务:
在Startup类里面的ConfigureServices方法里面添加注册
public void ConfigureServices(IServiceCollection services)
{
//注册IdentityServer
services.AddAuthentication(config => {
config.DefaultScheme = "Bearer"; //这个是access_token的类型,获取access_token的时候返回参数中的token_type一致
}).AddIdentityServerAuthentication(option => {
option.ApiName = "FirstAPI"; //资源名称,认证服务注册的资源列表名称一致,
option.Authority = "http://127.0.0.1:5000"; //认证服务的url
option.RequireHttpsMetadata = false; //是否启用https
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
然后在在Startup的Configure方法里配置Authentication中间件:
//配置Authentication中间件
app.UseAuthentication();
然后添加一个控制器进行验证测试:
我这里写了一个获取value值简单检测一下。
// GET api/values
[HttpGet]
[Authorize]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
这里注意要添加[Authorize]特性。用来做验证是否有权限的。没有的话,以上做的没有意义。需要引用命名空间:using Microsoft.AspNetCore.Authorization;
看一下正确的请求结果:

如果不传token值请求:

注意这里就会返回401的未授权状态。
创建Client(客户端)
上面我们使用的是postman请求的以演示程序是否创建成功,这里我们假设一个用户的使用客户端,这里我们创建一个控制台来模拟一下真实的小场景。
既然是控制台就没什么好说的直接上代码main函数:
Task.Run(async () =>
{
//从元数据中发现终结点,查找IdentityServer
var disco = await DiscoveryClient.GetAsync("http://127.0.0.1:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
//向IdentityServer请求令牌
var tokenClient = new TokenClient(disco.TokenEndpoint, "YbfTest", "YbfTest123");//请求的客户资源
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("FirstAPI");//运行的范围
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
//访问Api
var client = new HttpClient();
//把令牌添加进请求
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",tokenResponse.AccessToken);
//client.SetBearerToken(tokenResponse.AccessToken);
client.SetToken("Bearer", tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:42033/api/values");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
});
Console.ReadLine();
这里主要介绍一下请求资源时添加令牌主要有三种形式,我都在代码给出,根据api资源的注册形式选择适合的。api的注册我也写了两种形式。主要的区别就是token前面的Bearer,如果想写成自定义的和默认成Bearer就是这里的区分。
自定义就要使用:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",tokenResponse.AccessToken);
如果全局默认的形式就不比每次请求都要添加所以可以写成:
client.SetBearerToken(tokenResponse.AccessToken);
运行项目之后出现成功界面:

传送门
Flutter网络请求库DIO入门文档 - 掘金
一、dart:io提供的HttpClient
1、支持常用的Http操作,比如get,post等
2、异步操作,在io.dart中有相关描述
- This library allows you to work with files, directories,
- sockets, processes, HTTP servers and clients, and more.
- Many operations related to input and output are asynchronous
- and are handled using [Future]s or [Stream]s, both of which
- are defined in the [dart:async
- library](../dart-async/dart-async-library.html).
- To use the dart:io library in your code:
-
import 'dart:io'; 复制代码
3、网络调用通常遵循如下步骤:
创建 client. 构造 Uri. 发起请求, 等待请求,同时您也可以配置请求headers、 body。 关闭请求, 等待响应. 解码响应的内容. 4、示例,(httpbin.org 这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法)
_httpTest() async {
var url = 'https://httpbin.org/ip';
var httpClient = new HttpClient();
String result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
var data = jsonDecode(json);
result = data['origin'];
print(result);
} else {
result =
'Error getting IP address:\nHttp status ${response.statusCode}';
}
} catch (exception) {
result = 'Failed getting IP address';
}
}
输出结果:122.70.159.214
复制代码
二、dio
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等
1、安装
dependencies:
dio: ^3.0.10
2、常用请求
以下实验基于Charles的MapLocal,json文件内容为{“animal”:”dog”}
– get
Response response;
Dio dio = _dio();
response = await dio.get("http://test?id=1&name=dio1&method=get");
// 请求参数也可以通过对象传递,上面的代码等同于:
response = await dio.get("http://test", queryParameters: {"id": 2, "name": "dio2"});
print(response.data['animal']);
复制代码
– post
Response response;
Dio dio = _dio();
response = await dio.post("http://test?id=1&name=dio1&method=post");
// 请求参数也可以通过对象传递,上面的代码等同于:
response = await dio.post("http://test", queryParameters: {"id": 2, "name": "dio2"});
print(response.data['animal']);
//监听接收数据进度
response = await dio.post(
"http://test",
queryParameters: {"id": 2, "name": "dio2"},
onReceiveProgress: (int receive, int total) {
print("$receive $total");
},
);
复制代码
– 发起多个请求
Dio dio = _dio();
Future.wait([dio.post("http://test/test1"), dio.get("http://test/test2")]).then((e) {
print(e);
}).catchError((e) {});
结果为[{"animal":"dog"}, {"animal":"dog"}]
复制代码
– 下载文件
Dio dio = _dio();
Response response =
await dio.download("https://www.baidu.com/", "assets/data/test.html");
复制代码
– 上传文件
Response response;
Dio dio = _dio();
FormData formData;
formData = FormData.fromMap({
"animal": "dog",
});
response = await dio.post("http/test/upload", data: formData);
//上传多个文件
formData = FormData.fromMap({
"animal": "dog",
"files": [
await MultipartFile.fromFile("assets/data/test1.json", filename: "test1.json"),
await MultipartFile.fromFile("assets/data/test2.json", filename: "test2.json"),
]
});
response = await dio.post("http/test/upload", data: formData);
复制代码
3、配置dio
Dio dio = Dio();
// 你可以使用默认配置或传递一个可选 BaseOptions参数来创建一个Dio实例 :
// 配置dio实例
dio.options.baseUrl = "https://www.xx.com/api";
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
// 或者通过传递一个 `options`来创建dio实例
BaseOptions options = BaseOptions(
baseUrl: "https://www.xx.com/api",
connectTimeout: 5000,
receiveTimeout: 3000,
);
dio = Dio(options);
复制代码
4、请求配置
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 parameters
Map<String, dynamic /*String|Iterable<String>*/ > queryParameters;
}
复制代码
5、响应数据
当请求成功时会返回一个Response对象,它包含如下字段:
{
/// 响应数据,可能已经被转换了类型, 详情请参考Options中的[ResponseType].
T data;
/// 响应头
Headers headers;
/// 本次请求信息
Options request;
/// Http status code.
int statusCode;
/// 是否重定向(Flutter Web不可用)
bool isRedirect;
/// 重定向信息(Flutter Web不可用)
List<RedirectInfo> redirects ;
/// 真正请求的url(重定向最终的uri)
Uri realUri;
/// 响应对象的自定义字段(可以在拦截器中设置它),调用方可以在`then`中获取.
Map<String, dynamic> extra;
}
复制代码
6、拦截器
我们可以通过继承Interceptor来实现自定义的拦截器
每个 Dio 实例都可以添加任意多个拦截器,他们组成一个队列,拦截器队列的执行顺序是FIFO。通过拦截器你可以在请求之前或响应之后(但还没有被 then 或 catchError处理)做一些统一的预处理操作。
dio.interceptors.add(InterceptorsWrapper(
onRequest:(RequestOptions options) async {
// 在请求被发送之前做一些事情
return options; //continue
// 如果你想完成请求并返回一些自定义数据,可以返回一个`Response`对象或返回`dio.resolve(data)`。
// 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义数据data.
//
// 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象,或返回`dio.reject(errMsg)`,
// 这样请求将被中止并触发异常,上层catchError会被调用。
},
onResponse:(Response response) async {
// 在返回响应数据之前做一些预处理
return response; // continue
},
onError: (DioError e) async {
// 当请求失败时做一些预处理
return e;//continue
}
));
复制代码
7、拦截器中可以进行其他异步操作
dio.interceptors.add(InterceptorsWrapper(
onRequest:(Options options) async{
//...If no token, request token firstly.
Response response = await dio.get("/token");
//Set the token to headers
options.headers["token"] = response.data["data"]["token"];
return options; //continue
}
));
复制代码
8、Lock/unlock拦截器
你可以通过调用拦截器的 lock()/unlock 方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。
tokenDio = Dio(); //Create a instance to request the token.
tokenDio.options = dio.options;
dio.interceptors.add(InterceptorsWrapper(
onRequest:(Options options) async {
// If no token, request token firstly and lock this interceptor
// to prevent other request enter this interceptor.
dio.interceptors.requestLock.lock();
// We use a Dio(to avoid dead lock) instance to request token.
Response response = await tokenDio.get("/token");
//Set the token to headers
options.headers["token"] = response.data["data"]["token"];
dio.interceptors.requestLock.unlock();
return options; //continue
}
));
假设这么一个场景:我们需要给每个请求头中设置token,如果token不存在我们需要先请求token,获取到再继续请求,由于请求token过程是异步的,所以我们需要先锁定拦截器防止其他请求在没有获取到token的情况下进行网络请求,获取到token再解锁
复制代码
9、clear()方法来清空等待队列
dio.interceptors.clear()
复制代码
10、日志(开启后会打印request和response相关信息)
//由于拦截器队列的执行顺序是FIFO,如果把log拦截器添加到了最前面,则后面拦截器对options的更改就不会被打印(但依然会生效), 所以建议把log拦截添加到队尾。
dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
复制代码
11、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;
}
enum DioErrorType {
/// It occurs when url is opened timeout.
CONNECT_TIMEOUT,
/// It occurs when url is sent timeout.
SEND_TIMEOUT,
///It occurs when receiving timeout.
RECEIVE_TIMEOUT,
/// 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
/// read the DioError.error if it is not null.
DEFAULT
}
复制代码
12、CancelToken,取消请求
CancelToken token = CancelToken();
dio.post("/testpost?id=1&name=dio1&method=post",cancelToken: token).catchError((e) {
//我们会发现CancelToken提供了错误类型的判断,即此时CancelToken.isCancel(err)是true
//如
if (CancelToken.isCancel(err)) {
print("被取消啦");
}
}).then((data) {
return data;
});
token.cancel();
cancel_token.dart中源码也是判断DioErrorType
static bool isCancel(DioError e) {
return e.type == DioErrorType.CANCEL;
}
复制代码
13、dio和HttpClient关系
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 来发起网络请求。
扩展(适配器模式) 首页定义接口,接口中对要实现的功能加以抽象,然后定义不同的Adapter类来实现这个接口,Adapter类中是对接口中方法的不同实现,上层的调用代码不需要改变就可以随意切换对底层不同的功能调用。
14、设置代理
DefaultHttpClientAdapter 提供了一个onHttpClientCreate 回调来设置底层 HttpClient的代理,我们想使用代理,可以参考下面代码:
import 'package:dio/dio.dart';
import 'package:dio/adapter.dart';
...
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
// config the http client
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY localhost:8888";
};
// you can also create a HttpClient to dio
// return HttpClient();
};
复制代码
15、部分源码分析
dio.dart中
网络请求最终会调用到_request方法
当Response的泛型类为String且声明的ResponseType不为bytes和stream时
mergeOptions是将Dio的BaseOptions属性结合请求参数Options来生成一个RequestOptions对象,是最终发起网络请求的Options
Future<Response<T>> _request<T>(
String path, {
data,
Map<String, dynamic> queryParameters,
CancelToken cancelToken,
Options options,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress,
}) async {
if (_closed) {
throw DioError(error: "Dio can't establish new connection after closed.");
}
options ??= Options();
if (options is RequestOptions) {
data = data ?? options.data;
queryParameters = queryParameters ?? options.queryParameters;
cancelToken = cancelToken ?? options.cancelToken;
onSendProgress = onSendProgress ?? options.onSendProgress;
onReceiveProgress = onReceiveProgress ?? options.onReceiveProgress;
}
var requestOptions = mergeOptions(options, path, data, queryParameters);
requestOptions.onReceiveProgress = onReceiveProgress;
requestOptions.onSendProgress = onSendProgress;
requestOptions.cancelToken = cancelToken;
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
拦截器会判断
checkIfNeedEnqueue方法的作用就是判断是否有未处理完(判断是否处理完从而加锁是通过Completer实现的)的请求,如果有的话,本次请求需要排队等待之前的请求的完成(值得学习的是由于一次请求返回的是Future,所以这里利用了future.then((Callback))返回的还是一个Future对象的特点,巧妙的实现了多次请求的顺序执行而相互之间不会干扰),这里的then中的Callback回调就是checkIfNeedEnqueue的第二个参数。
// Convert the request/response interceptor to a functional callback in which
// we can handle the return value of interceptor callback.
Function _interceptorWrapper(interceptor, bool request) {
return (data) async {
var type = request ? (data is RequestOptions) : (data is Response);
var lock =
request ? interceptors.requestLock : interceptors.responseLock;
if (_isErrorOrException(data) || type) {
return listenCancelForAsyncTask(
cancelToken,
Future(() {
return checkIfNeedEnqueue(lock, () {
if (type) {
if (!request) data.request = data.request ?? requestOptions;
return interceptor(data).then((e) => e ?? data);
} else {
throw assureDioError(data, requestOptions);
}
});
}),
);
} else {
return assureResponse(data, requestOptions);
}
};
}
//如果拦截器被锁,接下来的request/response任务会进入一个队列,否则继续执行
FutureOr checkIfNeedEnqueue(Lock lock, EnqueueCallback callback) {
if (lock.locked) {
return lock.enqueue(callback);
} else {
return callback();
}
}
Future enqueue(EnqueueCallback callback) {
if (locked) {
// we use a future as a queue
return _lock.then((d) => callback());
}
return null;
}
//真正的网络请求,使用httpClientAdapter
//请求结果拿到后会查看interceptors.responseLock
//会监听是否有取消操作
// Initiate Http requests
Future<Response<T>> _dispatchRequest<T>(RequestOptions options) async {
var cancelToken = options.cancelToken;
ResponseBody responseBody;
try {
var stream = await _transformData(options);
responseBody = await httpClientAdapter.fetch(
options,
stream,
cancelToken?.whenCancel,
);
responseBody.headers = responseBody.headers ?? {};
var headers = Headers.fromMap(responseBody.headers ?? {});
var ret = Response(
headers: headers,
request: options,
redirects: responseBody.redirects ?? [],
isRedirect: responseBody.isRedirect,
statusCode: responseBody.statusCode,
statusMessage: responseBody.statusMessage,
extra: responseBody.extra,
);
var statusOk = options.validateStatus(responseBody.statusCode);
if (statusOk || options.receiveDataWhenStatusError) {
var forceConvert = !(T == dynamic || T == String) &&
!(options.responseType == ResponseType.bytes ||
options.responseType == ResponseType.stream);
String contentType;
if (forceConvert) {
contentType = headers.value(Headers.contentTypeHeader);
headers.set(Headers.contentTypeHeader, Headers.jsonContentType);
}
ret.data = await transformer.transformResponse(options, responseBody);
if (forceConvert) {
headers.set(Headers.contentTypeHeader, contentType);
}
} else {
await responseBody.stream.listen(null).cancel();
}
checkCancelled(cancelToken);
if (statusOk) {
return checkIfNeedEnqueue(interceptors.responseLock, () => ret);
} else {
throw DioError(
response: ret,
error: 'Http status error [${responseBody.statusCode}]',
type: DioErrorType.RESPONSE,
);
}
} catch (e) {
throw assureDioError(e, options);
}
}
//在io_adapter中
//dio中默认的adapter是DefaultHttpClientAdapter,其中HttpClient _defaultHttpClient,所以dio中的网络请求与底层HttpClient是通过DefaultHttpClientAdapter中对HttpClient网络请求的实现过程
class DefaultHttpClientAdapter implements HttpClientAdapter {
/// [Dio] will create HttpClient when it is needed.
/// If [onHttpClientCreate] is provided, [Dio] will call
/// it when a HttpClient created.
OnHttpClientCreate onHttpClientCreate;
HttpClient _defaultHttpClient;
bool _closed = false;
@override
Future<ResponseBody> fetch(
RequestOptions options,
Stream<List<int>> requestStream,
Future cancelFuture,
) async {
if (_closed) {
throw Exception(
"Can't establish connection after [HttpClientAdapter] closed!");
}
var _httpClient = _configHttpClient(cancelFuture, options.connectTimeout);
Future requestFuture = _httpClient.openUrl(options.method, options.uri);
.....(略)
}
复制代码
16、扩展:为什么需要通过代码设置代理才可以使用charles抓包呢?
因为当我们启动 Charles就是启动了一个HTTP代理服务器,这类工具会通知操作系统,“现在我在系统上创建了一个HTTP代理,IP为XXXXXX端口为XX。这个时候运行在系统上的http客户端再去发送请求的时候,他就不会再去进行DNS解析,去连接目标服务器,而是直接连接系统告诉他代理所在的地址。然后代理服务器会与客户端建立连接,再然后代理服务器根据请求信息再去连接真正的服务器。而在Flutter中的http.dart有如下说明
/**
- Sets the function used to resolve the proxy server to be used for
- opening a HTTP connection to the specified [url]. If this
- function is not set, direct connections will always be used.
- The string returned by [f] must be in the format used by browser
- PAC (proxy auto-config) scripts. That is either
-
"DIRECT" 复制代码 - for using a direct connection or
-
"PROXY host:port" 复制代码 - for using the proxy server [:host:] on port [:port:].
- A configuration can contain several configuration elements
- separated by semicolons, e.g.
-
"PROXY host:port; PROXY host2:port2; DIRECT" 复制代码 - The static function [findProxyFromEnvironment] on this class can
- be used to implement proxy server resolving based on environment
- variables. */ set findProxy(String f(Uri url));
//所以如果我们没有通过代码进行设置,就会直接请求到真正的服务器而不会走代理服务器
作者:wjq821
链接:https://juejin.cn/post/6959736964597678116
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
flutter使用http.dart处理网络请求_Walden_tinghou的博客-CSDN博客_dart 网络请求
来源: flutter使用http.dart处理网络请求_Walden_tinghou的博客-CSDN博客_dart 网络请求
1.flutter使用http.dart处理网络请求,首先要去这里查看最新版本 http.dart
接着在如图所示的地方填写最新的版本然后导入,这样就可以在项目中使用http.dart了
2.使用http.dart来处理网络请求,首先要在要用的文件中引入头文件,以后就可以使用http来发送请求
import ‘package:http/http.dart’ as http;

3.使用http处理项目网络请求
Future<List<Chat>> getDatas() async {
//不再是取消连接了!
_cancleConnect = false;
final response = await http
.get(Uri.parse(‘http://rap2api.taobao.org/app/mock/298265/api/chat/list’));
if (response.statusCode == 200) {
//获取相应数据,并转成Map类型!
final responseBody = json.decode(response.body);
//转模型数组 map中遍历的结果需要返回出去
List<Chat> chatList = responseBody[‘chat_list’].map<Chat>((item) {
return Chat.fromJson(item);
}).toList();
return chatList;
} else {
throw Exception(‘statusCode:${response.statusCode}’);
}
}
还可以使用如下代码打印请求返回的数据,便于我们开发调试
print(response.body);

————————————————
版权声明:本文为CSDN博主「Walden_tinghou」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sunnyboy9/article/details/122968023
dart - 执行 POST 请求时如何解决 Flutter CERTIFICATE_VERIFY_FAILED 错误? - IT工具网
来源: dart – 执行 POST 请求时如何解决 Flutter CERTIFICATE_VERIFY_FAILED 错误? – IT工具网
我正在 Dart 中发送一个发布请求。当我在 Postman 等 API 测试工具上对其进行测试时,它会给出响应。但是当我运行应用程序时。它给了我以下错误:-
E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363))
这是我的函数代码-
Future getAccessToken(String url) async {
try {
http.post('url',
body: {
"email": "xyz@xyz.example",
"password": "1234"
}).then((response) {
print("Reponse status : ${response.statusCode}");
print("Response body : ${response.body}");
var myresponse = jsonDecode(response.body);
String token = myresponse["token"];
});
} catch (e) {
print(e.toString());
}
这是完整的错误正文:
E/flutter ( 6264): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception: E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363)) E/flutter ( 6264): #0 IOClient.send (package:http/src/io_client.dart:33:23) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #2 BaseClient.post (package:http/src/base_client.dart:54:7) E/flutter ( 6264): #3 post.<anonymous closure> (package:http/http.dart:70:16) E/flutter ( 6264): #4 _withClient (package:http/http.dart:166:20) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #5 post (package:http/http.dart:69:5) E/flutter ( 6264): #6
_MyLoginFormState.getAccessToken (package:chart/main.dart:74:7) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #7
_MyLoginFormState.build.<anonymous closure> (package:chart/main.dart:64:29)
最佳答案
为了让 Flutter/Dart 的新手更清楚,为了在项目中全局启用此选项,您需要执行以下操作:
- 在您的 main.dart 文件中,添加或导入以下类:
class MyHttpOverrides extends HttpOverrides{
@override
HttpClient createHttpClient(SecurityContext? context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}
- 在您的主函数中,在函数定义之后添加以下行:
HttpOverrides.global = MyHttpOverrides();
This评论对解决这个问题很有帮助,请注意…
This should be used while in development mode, do NOT do this when you want to release to production, the aim of this answer is to make the development a bit easier for you, for production, you need to fix your certificate issue and use it properly, look at the other answers for this as it might be helpful for your case.
关于dart – 执行 POST 请求时如何解决 Flutter CERTIFICATE_VERIFY_FAILED 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54285172/
Android模拟器访问本地Web API - 简书
来源: Android模拟器访问本地Web API – 简书
http://localhost/api/test 和 http://192.168.0.8/api/test 都无法连接。
折腾了半天,包括修改本机的HOST文件,发现安卓虚拟机访问网络不受本机HOST文件的影响。这个时候才意识到,安卓虚拟机的本地网络跟我的电脑本地网络不同。
查了官方文档:Set up Android Emulator networkinghttps://developer.android.com/studio/run/emulator-networking.html

由此得知,在安卓模拟器上访问本地Web API的地址应该这样写:
http://10.0.2.2/api/test

1人点赞
作者:StormerX
链接:https://www.jianshu.com/p/028436e647d5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
android 模拟器访问本地服务器接口api_再回首我心依旧的博客-CSDN博客
来源: android 模拟器访问本地服务器接口api_再回首我心依旧的博客-CSDN博客
Android 模拟器访问本地api
最近因为公司网络的原因,暂时使用模拟器来进行调试(话说现在都是真机调试,基本上也不怎么使用模拟器了),在访问服务器接口时 遇到点问题。
org.apache.http.conn.HttpHostConnectException: Connection to http://192.168.2.23:80 refused
解决方案如下:
1,使用 win+r ,调处cmd 命令窗口
进入D:\DeveloperSoft\eclipseADT\Android-sdk-windows\platform-tools 路径下
运行adb shell
然后在运行getprop,结果如下:
2、得到模拟器的DNS地址
在结果里可以看到:
[net.dns1]: [10.0.2.3]
[net.dns2]: [10.0.2.4]
3、把dns改成我们自己的DNS
setprop net.dns1 192.168.1.1
如图
4,启动模拟器,然后进入 Settings->Wireless & networks->Mobile networks->Access Point Names
然后打开出现在列表中的access point;
然后下面这样设置:
– 代理: 你的本机ip地址
– 端口: 端口 一般80/8080
– 用户名: 如有需要添加 否则为null
– 密码: 如有需要添加 否则为null
如图所示
好,这样就可以使用模拟器来访问本机api了。
————————————————
版权声明:本文为CSDN博主「再回首我心依旧」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wuzhonghua1009/article/details/51200933
报错:flutter: Connection refused, errno = 111, address = 127.0.0.1, port = 38870_TonyChen-9102的博客-CSDN博客
报错:
flutter: Connection refused, errno = 111, address = 127.0.0.1, port = 38870
解决方法:
原因原来是127.0.0.1 和localhost 指向的是虚拟机的地址,并非电脑本地。
https://stackoverflow.com/questions/6760585/accessing-localhostport-from-android-emulator
所以我们只需要修改接口的127.0.0.1或localhost就行了;在Android操作系统中,将本地电脑映射为10.0.2.2;亦或使用电脑ip地址。
I am using Windows 10 as my development platform, accessing 10.0.2.2:port in my emulator is not working as expected, and the same result for other solutions in this question as well.
After several hours of digging, I found that if you add -writable-system argument to the emulator startup command, things will just work.
You have to start an emulator via command line like below:
emulator.exe -avd <emulator_name> -writable-system
Then in your emulator, you can access your API service running on host machine, using LAN IP address and binding port:
http://192.168.1.2:<port>
Hope this helps you out.
About start emulator from command line: https://developer.android.com/studio/run/emulator-commandline.
I had the same issue when I was trying to connect to my IIS .NET Webservice from the Android emulator.
- install
npm install -g iisexpress-proxy iisexpress-proxy 53990 to 9000to proxy IIS express port to 9000 and access port 9000 from emulator like"http://10.0.2.2:9000"
the reason seems to be by default, IIS Express doesn’t allow connections from network https://forums.asp.net/t/2125232.aspx?Bad+Request+Invalid+Hostname+when+accessing+localhost+Web+API+or+Web+App+from+across+LAN
[DioErrorType.DEFAULT]: SocketException: OS Error: Connection refused, errno = 111, address = local_编程的平行世界的博客-CSDN博客
SocketException: Connection refused (OS Error: Connection refused, errno = 111), address = localhost, port = 33352
Bug处理 — dio网络请求问题
报错
[DioErrorType.DEFAULT]: SocketException: OS Error: Connection refused, errno = 111, address = localhost, port = 53766
这个问题在学习Flutter 网络请求时非常常见
总结来说造成这个问题的出现有4种可能:
1.请求本地连接,ip地址错误
2.未添加网络请求权限
3.请求的地址是http,不是https
4.与服务端的请求参数不同,导致无法请求到接口
1.请求本地连接,ip地址错误
解决
我用的是 Android模拟器
之前请求的路径是:http://127.0.0.1:9090/getCourse
得改成:http://10.10.0.2:9090/getCourse 或者 http://你电脑的ip:9090/getCourse
解决~
2.未添加网络请求权限
对于网络请求我们是需要给予权限的
找到这个文件:
然后在:
下方加入
<uses-permission Android:name=”android.permission.READ_PHONE_STATE” />
<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />
然后先结束软件运行在重新运行就可以了
3.请求的地址是http,不是https
因为是Android9.0加入了所有的网络请求默认使用https,然而使用的是http,最终加入相应的配置才能完成http的请求。、
仍然在刚刚的文件中加入:android:usesCleartextTraffic=“true”
即可
android:usesCleartextTraffic 指示应用程序是否打算使用明文网络流量,例如明文HTTP。目标API级别为27或更低的应用程序的默认值为“ true”。面向API级别28或更高级别的应用默认为“ false”。
当属性设置为“ false”时,平台组件(例如,HTTP和FTP堆栈,DownloadManager和MediaPlayer)将拒绝应用程序使用明文流量的请求。强烈建议第三方库也采用此设置。避免明文通信的主要原因是缺乏机密性,真实性和防篡改保护;网络攻击者可以窃听所传输的数据,并且还可以对其进行修改而不会被检测到。
4.与服务端的请求参数不同,导致无法请求到接口
这就是我们与服务端的交互问题,如果请求参数错误,可能会得到状态码为400的情况,也是会提示这样的错误,所以请与服务端核对,
提醒一下,也许是Get请求的参数data没有写,可以写上data:{}
————————————————
版权声明:本文为CSDN博主「编程的平行世界」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/txaz6/article/details/119168489
将WebAPI部署在IIS上_斯 钦的博客-CSDN博客_webapi发布到iis
来源: 将WebAPI部署在IIS上_斯 钦的博客-CSDN博客_webapi发布到iis
这篇文章用的是VS2019中的ASP.NET Core Web应用程序中默认的.netcore3.1 WebAPI demo。用这个默认demo的原因就是,大家情况错综复杂,很难找到一篇适合自己的文章,也不知道自己究竟错在哪里,不好排除。用默认的demo来做展示,如果你可以做出来,可以保证你的错误不是由于你代码造成,可以帮助大家排除一部分问题,废话不多说,下面开始。
我使用的是VS2019社区版,WebAPI的版本是.netcore3.1,其他版本可能略有不同,请根据情况适当更改。
1.打开https://dotnet.microsoft.com/download/dotnet-core/3.1,点击Hosting Bundle下载安装,安装好后重启电脑。
2.打开IIS,双击模块。
3.查看是否存在ASP.NET Core Module v2,若不存在请重新安装。
4.打开VS2019,新建项目,选择ASP.NET Core Web应用程序,点击下一步。
5.随便起个名字,点击创建。
6.选择API,点击创建。
7.下拉,选择你项目的名字,然后运行。
8.运行成功后,会出现如下图所示:
9.在项目上右键,点击发布,选择文件夹,点击下一步。
10.点击完成。
11.点击发布,输出栏会显示发布成功。
13.网站名称随便写;应用程序池选择DefaultAppPool;物理路径就是第11步中,下方输出栏里显示的蓝色字体的发布路径(自己去选择路径,不要直接粘贴);类型选择https;端口号随便设置(不要设置成正在使用的端口);IIS证书选择IIS Express Development Certificate;设置好后,点击确定。
14.点击浏览。
15.出现如下图的错误:
16.找到发布的文件夹:
17.右键publish文件夹,选择属性,选择安全,点击编辑。
18.点击添加。
19.输入:计算机名\IIS_IUSRS(计算机名不知道的,右键桌面上“此电脑”快捷方式,选择属性,查看计算机名),点击检查名称,然后点击确定。
20.勾选读取权限,已勾选则不做改变,然后点击确定。
21.切换到常规栏,取消勾选“只读”复选框,点击确定。
22.点击确定。
23.再次浏览网站(第14步)。
24.在网站后添加 /weatherforecast ,刷新,成功!
————————————————
版权声明:本文为CSDN博主「斯 钦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44884379/article/details/112466063
Mikel
