flutter开发陷阱笔记:dio错误信息FormatException
Dart是一门年轻的语言,SDK还不够成熟,使用中存在很多陷阱。过去我们修复了使用代理导致空指针的问题,但这次我们遇到了使用 cookie 导致 FormatException 的问题。
问题描述
dio是Flutter中文网站上一个强大的开源Dart Http请求库。我使用这个库编写了一个访问网站登录界面的演示。
Dio client = new Dio(options);
client.interceptors.add(CookieManager(CookieJar()));
FormData formData = new FormData.from(
{"id": 123456, "passwd": 456789, "CookieDate": "2"});
var response = await client.post<String>(
"/path/to/login.json",
data: formData,
);
复制代码
代码很简单:
- 使用dio发起发送请求,包含用户名和密码,调用Web登录接口;
- 服务器响应包含用于设置登录信息的cookie;后续访问必须带上cookie。
- Client客户端使用CookieManager(CookieJar())来存储cookie的信息。
不幸的是,简单的代码遇到了以下错误:
DioError [DioErrorType.DEFAULT]:FormatException:Cookie名称中的无效字符,代码单元:'91'(位于字符5)main[UTMPID]Catch响应解析服务器:
set-cookie: main[UTMPUSERID]=guest;路径=/;域=.****.net
set-cookie: main[UTMPKEY]=48990095;路径=/;域=.****.net
set-cookie: main[UTMPNUM]=79117;路径=/; domain=.****.net
set-cookie字段包含非法字符“[”和“],导致请求失败。
问题发现,和服务器兄弟一起烧烤,换个cookie字符串定义,问题就解决了。^_^
但是作为一个有野心的程序员,我必须追根溯源。for cookie:
RFC6265 指定 cookie-6265-name 是 token。
RFC2616将token定义为没有分隔符的CHAR,因此“[”和“]”实际上是协议设置的非法字符。
看来烧烤是免费邀请T_T的,但协议和现实差距很大。
StackOverFlow 这个答案解释了合法cookie字符的变化历史。简单来说,RFC6265定义了最新的标准,新的网络接口应该符合这个标准。然而,存在许多历史问题,许多网站使用原始的 Netscape cookie_spec。因此,从实用的角度来看,新的服务器端接口必须符合RFC6265,客户端应向前兼容历史标准。
Dart SDK 中的处理
回到 Flutter 代码中,Dio 作为拦截器通过 CookieManager 拦截所有请求和响应。如果响应包含 cookie,则将其存储在 CookieJar 文件中;当发出请求时,将从 CookieJar 中检索当前的 cookie。
dio/lib/src/interceptors/cookie_mgr.dart
_saveCookies(Response response) {
......
cookieJar.saveFromResponse(
response.request.uri,
cookies.map((str) => Cookie.fromSetCookieValue(str)).toList(),
);
......
}
复制代码
因此,判断 Cookie 字段是否合法,Dart SDK 中包含的代码 => Cookie.fromSetCookieValue:
dart-sdk/lib/_http/http_headers.dart
void _validate() {
const separators = const [
"(",
")",
"<",
">",
"@",
",",
";",
":",
"\\",
'"',
"/",
"[",
"]",
"?",
"=",
"{",
"}"
];
for (int i = 0; i < name.length; i++) {
int codeUnit = name.codeUnits[i];
if (codeUnit <= 32 ||
codeUnit >= 127 ||
separators.indexOf(name[i]) >= 0) {
throw new FormatException(
"Invalid character in cookie name, code unit: '$codeUnit'",
name,
i);
}
}
// Per RFC 6265, consider surrounding "" as part of the value, but otherwise
// double quotes are not allowed.
int start = 0;
int end = value.length;
if (2 <= value.length &&
value.codeUnits[start] == 0x22 &&
value.codeUnits[end - 1] == 0x22) {
start++;
end--;
}
for (int i = start; i < end; i++) {
int codeUnit = value.codeUnits[i];
if (!(codeUnit == 0x21 ||
(codeUnit >= 0x23 && codeUnit <= 0x2B) ||
(codeUnit >= 0x2D && codeUnit <= 0x3A) ||
(codeUnit >= 0x3C && codeUnit <= 0x5B) ||
(codeUnit >= 0x5D && codeUnit <= 0x7E))) {
throw new FormatException(
"Invalid character in cookie value, code unit: '$codeUnit'",
value,
i);
}
}
}
复制代码
- 从上面的代码可以看出,Dart SDK 严格执行 RFC6265 标准,
- "("," )","","@",",",";",":","\",'"',"/","["," ] ","?" "=","{","}" 都是非法字符。
- 注意,在 Dart 2.1 之前的版本中,cookie 名称前后的双引号也会被视为非法字符。修复完成后已经解决了。
终端解决方法
因为服务端代码是继承的,不能修改。我们保证客户端的兼容性,兼容的方法是不使用提供的cookie通过Dart SDK,而是使用我们自定义的文件cookie,这样我们就可以自定义最后客户端的合法字符了。
Dio 符合协议要求。只是灰度处理的深浅还不够。毕竟,有大量的服务器应用程序仍然使用原来的定义。cookie中的_validate是一个私有方法。如果能够通过继承的方式暴露和修改,那么冗余代码量就会少很多。
作者:TonyBuilder
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。