import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:isekai_wiki/api/base_api.dart'; import 'package:isekai_wiki/global.dart'; import 'package:json_annotation/json_annotation.dart'; part 'mw_api.g.dart'; @JsonSerializable() class MWApiErrorException implements Exception { String code; String? info; @JsonKey(name: "*") String? detail; MWApiErrorException({required this.code, this.info, this.detail}); factory MWApiErrorException.fromJson(Map json) => _$MWApiErrorExceptionFromJson(json); Map toJson() => _$MWApiErrorExceptionToJson(this); @override String toString() { return "MediaWiki Api Error: ${info ?? code}"; } } class MWApiEmptyBodyException implements Exception { dynamic resData; MWApiEmptyBodyException(this.resData); @override String toString() { return "MediaWiki Api Response body is empty"; } } class MWResponse { List? warnList; T data; Map? continueInfo; MWResponse(this.data, {this.warnList, this.continueInfo}); MWResponse replaceData(N data) { return MWResponse( data, warnList: warnList, continueInfo: continueInfo, ); } } class MWApi { static Uri apiBaseUri = Uri.parse(Global.wikiApiUrl); static Future>> get(String action, {Map? query}) async { Map queryStr = query?.map((key, value) => MapEntry(key, value.toString())) ?? {}; queryStr.addAll({ "action": action, "format": "json", "formatversion": "2", "uselang": Global.wikiLang, }); if (Global.webOrigin != null) { queryStr["origin"] = Global.webOrigin!; } var resText = ""; try { resText = await BaseApi.get(apiBaseUri, search: queryStr); } on DioError catch (err) { if (err.type == DioErrorType.response) { if (err.response != null) { var response = err.response!; var contentType = response.headers[Headers.contentTypeHeader]; if (contentType != null && contentType.contains("application/json") && response.data is String) { resText = response.data as String; } } } if (resText.isEmpty) { // 没有捕获到服务器返回的错误,则抛给上一层 rethrow; } } return parseMWResponse(action, resText); } static MWResponse> parseMWResponse( String action, String resJson) { var resData = jsonDecode(resJson); if (resData is! Map) { throw MWApiEmptyBodyException(resData); } // 处理请求错误 if (resData.containsKey("error")) { throw MWApiErrorException.fromJson(resData["error"]!); } // 请求结果 if (!resData.containsKey(action) || resData[action] is! Map) { throw MWApiEmptyBodyException(resData); } MWResponse> mwRes = MWResponse(resData[action] as Map); // continue参数 var batchcomplete = resData["batchcomplete"]; if (batchcomplete is bool && batchcomplete) { var continueInfo = resData["continue"]; if (continueInfo is Map) { mwRes.continueInfo = {}; continueInfo.forEach((key, value) { var keyStr = key; mwRes.continueInfo![keyStr] = value.toString(); }); } } return mwRes; } }