You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.5 KiB
Dart
138 lines
3.5 KiB
Dart
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<String, dynamic> json) =>
|
|
_$MWApiErrorExceptionFromJson(json);
|
|
|
|
Map<String, dynamic> 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<T> {
|
|
List<dynamic>? warnList;
|
|
T data;
|
|
Map<String, String>? continueInfo;
|
|
|
|
MWResponse(this.data, {this.warnList, this.continueInfo});
|
|
|
|
MWResponse<N> replaceData<N>(N data) {
|
|
return MWResponse<N>(
|
|
data,
|
|
warnList: warnList,
|
|
continueInfo: continueInfo,
|
|
);
|
|
}
|
|
}
|
|
|
|
class MWApi {
|
|
static Uri apiBaseUri = Uri.parse(Global.wikiApiUrl);
|
|
|
|
static Future<MWResponse<Map<String, dynamic>>> get(String action,
|
|
{Map<String, dynamic>? query}) async {
|
|
Map<String, String> 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<Map<String, dynamic>> parseMWResponse(
|
|
String action, String resJson) {
|
|
var resData = jsonDecode(resJson);
|
|
if (resData is! Map<String, dynamic>) {
|
|
throw MWApiEmptyBodyException(resData);
|
|
}
|
|
|
|
// 处理请求错误
|
|
if (resData.containsKey("error")) {
|
|
throw MWApiErrorException.fromJson(resData["error"]!);
|
|
}
|
|
|
|
// 请求结果
|
|
if (!resData.containsKey(action) ||
|
|
resData[action] is! Map<String, dynamic>) {
|
|
throw MWApiEmptyBodyException(resData);
|
|
}
|
|
|
|
MWResponse<Map<String, dynamic>> mwRes =
|
|
MWResponse(resData[action] as Map<String, dynamic>);
|
|
|
|
// continue参数
|
|
var batchcomplete = resData["batchcomplete"];
|
|
if (batchcomplete is bool && batchcomplete) {
|
|
var continueInfo = resData["continue"];
|
|
if (continueInfo is Map<String, dynamic>) {
|
|
mwRes.continueInfo = {};
|
|
continueInfo.forEach((key, value) {
|
|
var keyStr = key;
|
|
mwRes.continueInfo![keyStr] = value.toString();
|
|
});
|
|
}
|
|
}
|
|
|
|
return mwRes;
|
|
}
|
|
}
|