http packageを使っている。 pub.dev
まあ、便利なんだけどtimeoutとcookieが対応していない。またUserAgentは常に指定する必要がある。 なので、それらに対応するには別packageを使うか自分で頑張る他ない。
諸々対応しているHTTP Clientとしてdioがあるが、自前で頑張っている+中国語でドキュメントが書かれていたりするので、今回は自分で頑張ることにした。
import 'dart:io'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:http/http.dart' as http; class HttpClient extends http.BaseClient { static const userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"; final http.Client _inner = http.Client(); final CookieJar _cookieJar = CookieJar(); final Duration _timeout = Duration(seconds: 10); @override Future<http.StreamedResponse> send(http.BaseRequest request) async { print("${request.method} ${request.url}"); // set User-Agent request.headers[HttpHeaders.userAgentHeader] = userAgent; final cookies = _cookieJar.loadForRequest(request.url); _removeExpiredCookies(cookies); String cookie = _getCookies(cookies); if (cookie.isNotEmpty) { request.headers[HttpHeaders.cookieHeader] = cookie; } final response = await _inner.send(request).timeout(_timeout); if (response != null && response.headers != null) { final cookieHeader = response.headers[HttpHeaders.setCookieHeader]; _saveCookies(response.request.url, cookieHeader); } return response; } void _removeExpiredCookies(List<Cookie> cookies) { cookies.removeWhere((cookie) { if (cookie.expires != null) { return cookie.expires.isBefore(DateTime.now()); } return false; }); } String _getCookies(List<Cookie> cookies) { return cookies.map((cookie) => "${cookie.name}=${cookie.value}").join('; '); } void _saveCookies(Uri uri, String cookieHeader) { if (cookieHeader == null || cookieHeader.isEmpty) { return; } // set-cookieが複数あった場合は、","でjoinして返ってくるので分割する必要がある final cookies = cookieHeader.split(","); if (cookies.isEmpty) { return; } _cookieJar.saveFromResponse( uri, cookies.map((cookie) => Cookie.fromSetCookieValue(cookie)).toList(), ); } }
という感じでやっている。 また何度もリクエストするようなコードを書いていると Connection closed before full header was received というエラーが出ることがあり、それは1秒ほどdelayすると起きない。
final response1 = await http.get(...); ... await Future<void>.delayed(Duration(seconds: 1)); ... // delayをしないとエラーになるが、delayするとエラーが起きない final response2 = await http.get(...);