Code前端首页关于Code前端联系我们

flutter开发陷阱笔记:dio错误信息FormatException

terry 2年前 (2023-09-23) 阅读数 69 #移动小程序

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。Flutter开发填坑笔记: dio报错 FormatException

RFC2616将token定义为没有分隔符的CHAR,因此“[”和“]”实际上是协议设置的非法字符。 Flutter开发填坑笔记: dio报错 FormatException

看来烧烤是免费邀请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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门