flutter api 自动生成基于source_gen和build_runner,网诺接口,基于swagger。文笔不行,能贴图和贴代码的,绝对不多BB。代码中都有注释,代码中的敏感信息全部替换了,但是,代码全部分享出来了。
一 项目目录以及脚本工程图示
1.1 api_code_generator 工程代码以及项目结构
ApiSite 类,用来在主工程中注解,生成对应的服务下的api
class ApiSite {
final String serviceName;
final String version;
const ApiSite({this.serviceName, this.version});
}
1.2 api_code_generator_gen 工程代码以及项目结构
yaml 依赖文件代码
name: api_code_generator_gen #项目命名,主工程需要用到
description: A new Flutter package.
version: 0.0.1
author:
homepage:
environment:
sdk: ">=2.2.0 <3.0.0"
dependencies:
source_gen: ^0.9.6
flutter:
sdk: flutter
api_code_generator:
path: ../api_code_generator/
dio: ^3.0.6
dev_dependencies:
build_runner: ^1.10.0
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
build.yaml 文件,该文件为配置文件
targets:
$default: #定义目标库,关键字$default默认为当前库
builders: #构建的两个库
api_code_generator|api_code_generator_gen:
enabled: true #可选,是否将构建器应用于此目标
builders:
code_generator:
target: ":api_code_generator_gen" #目标库
import: "package:api_code_generator_gen/builder.dart" #build文件
builder_factories: ["apiCodeGenerator"] #build文件中对应的方法
build_extensions: {".dart": [".api_code_generator_gen.g.part"]}
auto_apply: dependents #将此Builder应用于包,直接依赖于公开构建起的包
build_to: cache #输出转到隐藏的构建缓存,不会发布
applies_builders: ["source_gen|combining_builder"] #指定是否可以延迟运行构建器
builder.dart 文件代码
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'api_code_generator_gen.dart';
//code_generator 用于区别其它库
Builder apiCodeGenerator(BuilderOptions options) =>
SharedPartBuilder([ApiCodeGenerator()], 'api_code_generator');
api_code_generator_gen.dart 文件代码
library api_code_generator_gen;
export 'src/api_code_generator_gen_base.dart';
api_response.dart 文件代码 swagger 返回的api 数据实体,蛋疼,全部手解
import 'dart:math';
class ApiResponse {
String version;
String swagger;
String host;
String basePath;
Info info;
List<Tags> tags;
Map<String, Map<String, PathInfo>> paths;
Map<String, Definitions> definitions;
ApiResponse.fromJson(Map<String, dynamic> json) {
this.swagger = json['swagger'];
this.host = json['host'];
this.basePath = json['basePath'];
this.info = Info.fromJson(json['info']);
var pathsJson = json['paths'];
if (pathsJson != null) {
this.paths = Map<String, Map<String, PathInfo>>();
(pathsJson as Map).forEach((key, value) {
if (value != null) {
var m = Map<String, PathInfo>();
(value as Map).forEach((k2, v2) {
m[k2 as String] = PathInfo.fromJson(v2);
});
this.paths[key as String] = m;
}
});
}
var definitionsJson = json['definitions'];
if (definitionsJson != null) {
this.definitions = Map();
(definitionsJson as Map).forEach((key, value) {
this.definitions[key] = Definitions.fromJson(value);
});
}
var tagsJson = json['tags'];
if (tagsJson != null) {
this.tags = List<Tags>();
(tagsJson as List).forEach((v) {
this.tags.add(Tags.fromJson(v));
});
}
}
}
class Info {
String description;
String version;
Info.fromJson(Map<String, dynamic> json) {
this.description = json['description'];
this.version = json['version'];
}
}
class Tags {
String name;
String description;
Tags.fromJson(Map<String, dynamic> json) {
this.name = json['name'];
this.description = json['description'];
}
}
class PathInfo {
List<String> tags;
String summary;
String operationId;
List<String> produces;
List<Parameters> parameters;
Map<String, Responses> responses;
PathInfo.fromJson(Map<String, dynamic> dynamic) {
var tagsJson = dynamic['tags'];
if (tagsJson != null) {
this.tags = List<String>();
(dynamic['tags'] as List).forEach((v) {
this.tags.add(v);
});
}
this.summary = dynamic["summary"];
this.operationId = dynamic["operationId"];
var producesJson = dynamic['produces'];
if (producesJson != null) {
this.produces = List<String>();
(producesJson as List).forEach((v) {
this.produces.add(v);
});
}
var parametersJosn = dynamic['parameters'];
if (parametersJosn != null) {
this.parameters = List<Parameters>();
(parametersJosn as List).forEach((v) {
this.parameters.add(Parameters.fromJson(v));
});
}
var responsesJson = dynamic['responses'];
if (responsesJson != null) {
this.responses = Map();
(responsesJson as Map).forEach((key, value) {
this.responses[key] = Responses.fromJson(value);
});
}
}
}
class Parameters {
String name;
String ins;
String description;
bool required;
String type;
dynamic defaults;
String format;
List<String> enums;
String typeName;
Parameters items;
String $ref;
Object schema;
Parameters.fromJson(Map<dynamic, dynamic> dynamic) {
this.name = dynamic["name"];
this.ins = dynamic["in"];
this.description = dynamic["description"];
this.required = dynamic["required"];
this.type = dynamic["type"];
this.defaults = dynamic["default"];
this.format = dynamic["format"];
this.$ref = dynamic['\$ref'];
if (dynamic.containsKey("enums") && dynamic["enum"] != null) {
this.enums = List();
(dynamic["enum"] as List).forEach((f) => {this.enums.add(f)});
}
if (dynamic.containsKey("items") && dynamic["items"] != null) {
this.items = Parameters.fromJson(dynamic["items"]);
}
if (dynamic.containsKey("schema") && dynamic["schema"] != null) {
var schemaJson = dynamic["schema"];
if (schemaJson is Map) {
this.schema = Map();
schemaJson.forEach((k, v) {
(schema as Map)[k] = v;
});
} else if (schemaJson is String) {
this.schema = schemaJson;
}
}
}
}
class Responses {
String description;
Parameters schema;
Responses.fromJson(Map<String, dynamic> dynamic) {
this.description = dynamic["description"];
var schemaJson = dynamic["schema"];
if (schemaJson != null) {
this.schema = Parameters.fromJson(schemaJson);
}
}
}
class Definitions {
String type;
Map<String, Parameters> properties;
String title;
String description;
Definitions.fromJson(Map<String, dynamic> dynamic) {
this.type = dynamic["type"];
this.title = dynamic["title"];
this.description = dynamic["description"];
var propertiesJson = dynamic['properties'];
if (propertiesJson != null) {
this.properties = Map();
(propertiesJson as Map).forEach((key, value) {
this.properties[key] = Parameters.fromJson(value);
});
}
}
}
class HttpFunction {
String className;
String typeName;
String url;
String method;
List<Parameters> parameters;
String summary;
}
class TsType {
String typeName;
String description;
String title;
Map<String, Parameters> properties;
}
api_code_generator_gen_base.dart 文件代码,画重点,里面是基于GeneratorForAnnotation 的代码自动生成的逻辑,吐槽一下,dart 写这个脚本真的没有Android基于 gradle plugin 用 groovey写的爽,groovey里面封装的网诺请求,直接可以解析成需要的实体map,而且,groovey是脚本语言,对象的属性字段,取用非常灵活。
import 'dart:math';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/src/builder/build_step.dart';
import 'package:dio/dio.dart';
import 'package:source_gen/source_gen.dart';
import 'package:api_code_generator/index.dart';
import './api_response.dart';
/*
* flutter packages pub run build_runner build
*/
class ApiCodeGenerator extends GeneratorForAnnotation<ApiSite> {
@override
generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
print("element is $element");
if (element is! ClassElement) {
throw InvalidGenerationSourceError(
"Request class is not ok for ${element.displayName}");
}
var classElement = (element as ClassElement);
var metadatas = classElement.metadata;
var apiSiteMeta = metadatas
.firstWhere((e) => (e.computeConstantValue().type.name == "ApiSite"));
var apiSiteConstantValue = apiSiteMeta.computeConstantValue();
var serviceName =
apiSiteConstantValue.getField("serviceName").toStringValue();
var version = apiSiteConstantValue.getField("version").toStringValue();
print("ApiSite apiName $serviceName");
var singGenerator = SingGenerator();
var code = await singGenerator.buildSing(serviceName, version);
return code;
}
}
class SingGenerator {
var apiPaths = Map<String, Map<String, HttpFunction>>();
Map<String, TsType> typeDefinitionMap = Map();
Future<String> buildSing(String serviceName, String version) async {
var apiResponse = await _getApi(serviceName);
apiResponse.version = version;
return _buildCode(apiResponse).toString();
}
Future<ApiResponse> _getApi(String serviceName) async {
var dio = Dio()
..options = BaseOptions(
baseUrl: "对不起,baseUrl 我不能提供",
connectTimeout: 10000,
receiveTimeout: 10000)
..interceptors
.add(LogInterceptor(responseBody: false, requestBody: false));
var url = "/$serviceName/v2/api-docs";
Response<dynamic> response = await dio.get(url);
return ApiResponse.fromJson(response.data);
}
StringBuffer _buildCode(ApiResponse apiResponse) {
var stringBuffer = StringBuffer();
_buildDescription(apiResponse, stringBuffer);
_buildRef(apiResponse);
_buildDefinitionMap(apiResponse);
_buildApiCode(stringBuffer);
_buildEntityCode(stringBuffer);
return stringBuffer;
}
/// 生成注解
void _buildDescription(ApiResponse apiResponse, StringBuffer stringBuffer) {
stringBuffer.write("/*");
stringBuffer.write("\n");
stringBuffer.write("basePath: " + apiResponse.basePath);
stringBuffer.write("\n");
stringBuffer.write("description: " + apiResponse.info.description);
stringBuffer.write("\n");
stringBuffer.write("*/");
}
///生成API文件
void _buildApiCode(StringBuffer stringBuffer) {
void buildSingApi(String methodName, HttpFunction httpFunction) {
var summary = httpFunction.summary;
var parameters = httpFunction.parameters;
var method = httpFunction.method;
var typeName = httpFunction.typeName;
var paramsQuery = [];
if (summary != null && summary.isNotEmpty) {
stringBuffer.writeln(" /*");
stringBuffer.writeln(" * $summary");
stringBuffer.writeln(" */");
}
String data = "";
var paramsPath = Map<String, String>();
parameters?.forEach((p) {
var nameAndType = "${p.name}: ${p.typeName}";
if (p.ins == 'query') {
paramsQuery.add(nameAndType);
} else if (p.ins == 'body') {
data = p.typeName;
} else if (p.ins == 'path') {
if (p.name != "version") {
paramsPath[p.name] = p.typeName;
}
} else if (p.ins == 'formData') {}
});
if (parameters != null && parameters.isNotEmpty) {
stringBuffer.writeln(" /*");
parameters.forEach((p) {
if (p.ins == "header") {
return;
}
if (p.name == "version") {
return;
}
var required = p.required ? '必填' : '选填';
var nameAndType = "${p.ins} ${p.name}:${p.typeName}($required)";
stringBuffer.writeln(" $nameAndType");
});
stringBuffer.writeln(" */");
}
var paramsComment = "";
var params = "";
if (method == "get") {
if (paramsQuery.isNotEmpty) {
paramsComment += "Map<String,dynamic> params";
params = ",queryParameters:params";
}
} else if (method == "post" || method == "delete" || method == "put") {
paramsComment += "$data data";
params = ",data";
}
//复杂对象,调用方写josn解析器
if (typeName.startsWith("Map<")) {
paramsComment += ",{Function(dynamic dynamic) beanCreator}";
}
stringBuffer.writeln(
" static Future<BaseEntrty<$typeName>> $methodName($paramsComment) async{");
stringBuffer.writeln(
" Response<dynamic> response = await HttpRequest.instance");
stringBuffer.writeln(".$method('${httpFunction.url}'$params);");
stringBuffer.writeln(
"return BaseEntrty<$typeName>.fromJson(response.data,(dynamic)=>");
if (typeName == "bool" ||
typeName == "int" ||
typeName == "double" ||
typeName == "String") {
stringBuffer.writeln("dynamic as $typeName");
} else if (typeName.startsWith("Map<")) {
stringBuffer.writeln("beanCreator(dynamic)");
} else if (typeName.startsWith("PageList<")) {
var beanType = typeName
.replaceAll("PageList", "")
.replaceFirst("<", "")
.replaceFirst(">", "");
var code = _isBasics(beanType)
? "dynamic as $beanType"
: "$beanType.fromJson(dynamic)";
stringBuffer.writeln(
"PageList<$beanType>.fromJson(dynamic, (dynamic) =>$code)");
} else if (typeName.startsWith("List<")) {
var beanType = typeName
.replaceAll("List", "")
.replaceFirst("<", "")
.replaceFirst(">", "");
var code = _isBasics(beanType)
? "dynamic as $beanType"
: "$beanType.fromJson(dynamic)";
stringBuffer.writeln("$code , listCreator : () => List<$beanType>()");
} else {
stringBuffer.writeln("$typeName.fromJson(dynamic)");
}
stringBuffer.writeln(");");
stringBuffer.writeln("}");
}
this.apiPaths?.forEach((className, entity) {
stringBuffer.writeln("class ${_toUpperCaseOne(className)}Server {");
entity.forEach(buildSingApi);
stringBuffer.writeln("}");
});
}
/// 生成实体文件
void _buildEntityCode(StringBuffer stringBuffer) {
//from json 方法
void _buildFromJsonFunction(
String className, Map<String, Parameters> properties) {
//
var beanCreator = "";
properties?.forEach((filedName, parameters) {
if (parameters.typeName == "T") {
beanCreator = "Function(dynamic dynamic) ${filedName}Creator";
beanCreator = "," + beanCreator;
}
});
stringBuffer.writeln("");
stringBuffer.writeln(
"${className.replaceAll("<T>", "")}.fromJson(Map<dynamic, dynamic> dynamic $beanCreator) {");
properties?.forEach((filedName, parameters) {
var ref = parameters.$ref;
if (ref != null && ref.isNotEmpty) {
if (parameters.typeName == "T") {
stringBuffer.writeln(
"this.$filedName=${filedName}Creator(dynamic['$filedName']);");
} else {
stringBuffer.writeln(
"this.$filedName=${parameters.typeName}.fromJson(dynamic['$filedName']);");
}
} else {
switch (parameters.type) {
case 'array':
case 'list':
stringBuffer
.writeln("var ${filedName}Json = dynamic['$filedName'];");
stringBuffer.writeln("if (${filedName}Json != null) {");
stringBuffer.writeln("this.$filedName = List();");
stringBuffer.writeln(
"(dynamic['$filedName'] as List).forEach((f) => this.$filedName.add(f));");
stringBuffer.writeln("}");
break;
// case 'object':
// stringBuffer.writeln(
// " this.$filedName=${parameters.typeName}.fromJson(dynamic['$filedName']);");
// break;
default:
stringBuffer.writeln("this.$filedName=dynamic['$filedName'];");
break;
}
}
});
stringBuffer.writeln("}");
}
void _buildFiled(String name, Parameters parameters) {
stringBuffer.writeln(" /*");
stringBuffer.writeln(" description: ${parameters?.description}");
stringBuffer.writeln(" type : ${parameters?.type}");
stringBuffer.writeln(" */");
//print(" ${parameters?.typeName} $name ;");
stringBuffer.writeln("");
stringBuffer.writeln(" ${parameters?.typeName} $name ;");
}
void _buildSingEntity(String name, TsType tsType) {
stringBuffer.writeln("/*");
stringBuffer.writeln("description ${tsType.description}");
stringBuffer.writeln("*/");
stringBuffer.writeln("class $name {");
tsType.properties?.forEach(_buildFiled);
//构造方法
stringBuffer.writeln("${name.replaceAll("<T>", "")}();");
//fromJson 方法
_buildFromJsonFunction(name, tsType.properties);
stringBuffer.writeln("}");
}
typeDefinitionMap.forEach(_buildSingEntity);
}
void _buildRef(ApiResponse apiResponse) {
var paths = apiResponse.paths;
var version = apiResponse.version;
/**
* 解析具体的方法参数
*/
void _analysisMethodInfo(
String path, List<String> pathParts, String method, PathInfo info) {
var produces = info.produces;
if (produces.contains("application/octet-stream")) {
return;
}
var tags = info.tags;
if (tags.contains("ignore")) {
return;
}
var className = "";
if (path.contains('/action/')) {
className = pathParts[pathParts.length - 3];
} else if (pathParts[pathParts.length - 1].contains('{')) {
className = pathParts[pathParts.length - 2];
} else {
className = pathParts[pathParts.length - 1];
}
className = _transformCamelCase(className);
if (className == 'geetest') {
return;
}
if (apiPaths[className] == null) {
apiPaths[className] = Map();
}
var responseOk = info.responses["200"];
try {
var ref = responseOk.schema.$ref;
if (ref == null || ref.isEmpty) {
ref = responseOk.schema?.items?.$ref;
}
if (ref == null || ref.indexOf('Response«') < 0) {
throw new Exception("接口返回类型出错");
}
var typeName = _formatType(_getType(responseOk.schema));
var methodName = path.contains('/action/')
? _transformCamelCase(pathParts[pathParts.length - 1])
: method;
if (apiPaths[className][methodName] != null) {
var result = apiPaths[className][methodName];
var message = "解析出相同的方法名 $result,$path";
throw new Exception(message);
}
var parameters = info.parameters;
parameters.forEach((it) {
it.typeName = _formatType(_getParametersType(it));
});
var summary = info.summary;
var httpFunction = HttpFunction();
httpFunction.className = className;
httpFunction.typeName = typeName;
httpFunction.url = path.replaceFirst("{version}", version);
httpFunction.method = method;
httpFunction.parameters = parameters;
httpFunction.summary = summary;
apiPaths[className][methodName] = httpFunction;
} catch (e) {
var message = "解析出错 $e path:$path responseSchema:$responseOk";
// throw `${ex}${this.serviceName} path:${path} responseSchema:${JSON.stringify(responseOk)}`
throw new Exception(message);
}
}
/**
* 解析path info
*/
void _analysisPathInfo(String path, Map<String, PathInfo> pathInfo) {
print("path $path");
var pathParts = path.split('/');
pathInfo.forEach((method, info) =>
{_analysisMethodInfo(path, pathParts, method, info)});
}
//去除不符合条件的API
paths.removeWhere((key, value) => (key == null ||
key.isEmpty ||
!key.contains('/{version}/') ||
key.contains('/pv/') ||
key.contains('/pvs/') ||
key.contains('pb/images/action/download') ||
value.isEmpty));
paths.forEach((path, pathInfo) => _analysisPathInfo(path, pathInfo));
}
void _buildDefinitionMap(ApiResponse apiResponse) {
var definitions = apiResponse.definitions;
TsType _buildTsType(String name, Definitions definitions) {
var tsType = TsType();
tsType.typeName = name;
tsType.description = definitions.description;
tsType.title = definitions.title;
tsType.properties = definitions.properties;
definitions.properties?.forEach((properties, parameters) {
if (name.contains("<T>") && properties == "data") {
parameters.typeName = "T";
} else {
var typeName = _getType(parameters);
parameters.typeName = typeName;
}
});
return tsType;
}
definitions.removeWhere((key, value) {
return key.contains("PageList«") ||
key.contains("PageRequest«") ||
key.contains("Response«") ||
key.contains("ObjectNode") ||
key.contains("List«") ||
key.contains("Map«");
});
definitions?.forEach((name, vo) {
var regExp = new RegExp(r"«.*»");
if (name.contains(regExp)) {
name = name.replaceAll(regExp, "<T>");
if (!typeDefinitionMap.containsKey(name)) {
typeDefinitionMap[name] = _buildTsType(name, vo);
}
} else {
typeDefinitionMap[name] = _buildTsType(name, vo);
}
});
}
String _formatType(String type) {
if (type == "string") {
return "String";
}
if (type.toLowerCase() == "boolean") {
return "bool";
}
if (type.toLowerCase() == "long") {
return "int";
}
if (type.toLowerCase() == "bigdecimal") {
return "double";
}
return type
.replaceAll("<long", "<int")
.replaceAll("<Long", "<int")
.replaceAll("<Double", "<double")
.replaceAll("<float", "<double")
.replaceAll("<Float", "<double")
.replaceAll("<string", "<String")
.replaceAll("<Boolean", "<bool")
.replaceAll("<boolean>", "<bool");
}
bool _isBasics(String type) {
return type == "int" ||
type == "double" ||
type == "String" ||
type == "int" ||
type == "bool";
}
String _getType(Object parameters) {
var refKey = '\$ref';
String _getRefType(String refStr) {
var refType = refStr.replaceFirst('#/definitions/', '');
refType = refType.replaceFirst('Response«', '').replaceFirst('»', '');
refType = refType.replaceAll("«", "<").replaceAll("»", ">");
if (refType.contains("<") && !refType.contains(">")) {
refType += ">";
}
return refType;
}
if (parameters is String) {
return _getRefType(parameters);
}
if (parameters is Map && parameters.containsKey(refKey)) {
var ref = parameters[refKey] as String;
return _getRefType(ref);
}
if (parameters is Map) {
print("parameters is map " + parameters.toString());
return "";
}
return _getParametersType(parameters as Parameters);
}
String _getParametersType(Parameters parameters) {
var ref = parameters.$ref;
if (ref != null && ref.isNotEmpty) {
return _getType(ref);
}
var schema = parameters.schema;
if (schema != null) {
if (schema is Map) {
return _getType(schema);
}
}
var type = parameters.type;
var format = parameters.format;
var items = parameters.items;
switch (type) {
case 'int':
case 'int32':
case 'integer':
case 'number':
case 'long':
case 'int64':
if (format == "double" || format == "float") {
return "double";
}
return "int";
case 'bigdecimal':
case 'double':
case 'float':
return 'double';
case 'string':
return 'String';
case 'boolean':
return 'bool';
case 'array':
case 'list':
var refType = _getParametersType(items);
return "List<$refType>";
case 'object':
return 'Object';
default:
return "";
}
}
String _transformCamelCase(String str) {
var array = str.split("-");
String result = "";
for (int i = 0; i < array.length; i++) {
var indexValue = array[i];
if (i != 0) {
var index0 = indexValue.substring(0, 1);
indexValue = indexValue.replaceFirst(index0, index0.toUpperCase());
}
result += indexValue;
}
return result;
}
String _toUpperCaseOne(String string) {
return "${string.substring(0, 1).toUpperCase()}${string.substring(1)}";
}
}
上面的代码也没什么好多的,就是JSON 解析,生成代码的逻辑。
二:在主工程中的应用。
主工程的yaml文件需要做以下配置
dev_dependencies:
build_runner: ^1.10.0#build_runner 用来运行命令行
flutter_test:
sdk: flutter
api_code_generator_gen:#脚本依赖
path: ./api_code_generator_gen/
dependencies:
flutter:
sdk: flutter
dio: ^3.0.6
flutter_bloc:
api_code_generator:#注解依赖
path: ./api_code_generator/
主工程中的网诺框架采用dio,因为我是做Android 开发的,里面的所有实体封装,都是基于java 面向对象的思想,dart 的数据解析,没人教,不知道,只能自己摸索,悲哀。自动生成的api 基于下面的网诺框架和实体类。
import 'package:dio/dio.dart';
class HttpRequest {
static Dio dio;
static HttpRequest get instance => _getInstance();
static HttpRequest _instance;
static HttpRequest _getInstance() {
if (_instance == null) {
_instance = new HttpRequest._internal();
}
return _instance;
}
HttpRequest._internal() {
dio = Dio()
..options = BaseOptions(
baseUrl: "bserUrl",
connectTimeout: 10000,
receiveTimeout: 10000)
..interceptors.add(LogInterceptor(responseBody: true, requestBody: true))
..interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
var headers = options.headers;
headers["appversion"] = "1.1.1";
return options;
}));
}
get(String path, {Map<String, dynamic> queryParameters}) async {
if (queryParameters == null || queryParameters.isEmpty) {
return dio.get(path);
}
await dio.get(path, queryParameters: queryParameters);
}
post(String path, dynamic data) async {
Options options = Options();
options.contentType = "";
options.method = "POST";
return await dio.post(path, data: data, options: options);
}
delete(String path, dynamic data) async {
Options options = Options();
options.contentType = "";
options.method = "DELETE";
return await dio.post(path, data: data, options: options);
}
put(String path, dynamic data) async {
Options options = Options();
options.contentType = "";
options.method = "PUT";
return await dio.post(path, data: data, options: options);
}
}
基于服务端数据封装的标准实体类
base_entrty.dart 标准实体类,该实体类,解析泛型的方法,全部暴露出去,泛型强转,dart报错,跟java 有区别,包括泛型为List的泛型,new List() 也必须暴露出去,在外面加泛型,不然,强转也报错,悲哀,我也搞不懂是为什么。
import 'dart:math';
class BaseEntrty<T> {
BaseEntrty();
T data;
bool status;
String code;
String msg;
String errorMsg;
int currentTime;
BaseEntrty.fromJson(
Map<String, dynamic> json, Function(dynamic dynamic) beanCreator,
{Function() listCreator}) {
this.status = json["status"];
this.code = json["code"];
this.msg = json["msg"];
this.errorMsg = json["errorMsg"];
this.currentTime = json["currentTime"];
dynamic jsonData = json["data"];
if (jsonData != null) {
if (jsonData is List<dynamic>) {
//获取具有实际泛型的list集合,不然list as T 会报错
List list = listCreator();
jsonData.forEach((v) {
list.add(beanCreator(v));
});
this.data = list as T;
} else {
var bean = beanCreator(jsonData);
this.data = bean as T;
}
}
}
}
base_page_entity.dart 分页接口标准实体
class PageList<T> {
List<T> entities;
int pageNo;
int count;
int pageSize;
//dynamic excludeFields;
PageList({
this.entities,
this.pageNo,
this.count,
this.pageSize,
// this.excludeFields
});
PageList.fromJson(
Map<String, dynamic> json, Function(dynamic dynamic) beanCreator) {
if (json['entities'] != null) {
entities = List<T>();
(json['entities'] as List).forEach((v) {
entities.add(beanCreator(v) as T);
});
}
pageNo = json['pageNo'];
count = json['count'];
pageSize = json['pageSize'];
// excludeFields = json['excludeFields'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
// if (this.entities != null) {
// data['entities'] = this.entities.map((v) => v.toJson()).toList();
// }
data['pageNo'] = this.pageNo;
data['count'] = this.count;
data['pageSize'] = this.pageSize;
// data['excludeFields'] = this.excludeFields;
return data;
}
}
class PageRequest<T> {
/* 参数对象 */
T param;
/* 页码 */
/* 参数格式: int32 */
int pageNo;
/* 每页条数 */
/* 参数格式: int32 */
int pageSize;
/* 开始时间 */
String startTime;
/* 截止时间 */
String endTime;
/* 时间范围 */
String timeRange;
}
应用(画重点)
定义一个文件,例如api_platform_behavior.dart,代码中的part 'api_platform_behavior.g.dart'; 为自动生成的文件的目标文件,ApiSite为注解,自动生成会基于该注解。
import 'package:api_code_generator/index.dart';//导入网诺框架包以及实体,自动生成的代码,会自动导入包里面的代码
import 'package:nutritionplan/http/http.dart';
part 'api_platform_behavior.g.dart';
@ApiSite(serviceName: "platform-behavior", version: "v5.0")
class PlatformBehavior {}
在 命令行中输入,脚本自动运行,会生成api_platform_behavior.g.dart文件
flutter packages pub run build_runner build
下面贴出部分生成的代码,url 我全部屏蔽了,希望谅解
part of 'api_platform_behavior.dart';
class CommentInfoServer {
/*
query commentKid:String(必填)
query pageNo:int(选填)
query pageSize:int(选填)
*/
// BaseEntrty<PageList<T>> 示例
static Future<BaseEntrty<PageList<CommentContentDTO>>> searchChild(
Map<String, dynamic> params) async {
Response<dynamic> response = await HttpRequest.instance.get(
'/url',
queryParameters: params);
return BaseEntrty<PageList<CommentContentDTO>>.fromJson(
response.data,
(dynamic) => PageList<CommentContentDTO>.fromJson(
dynamic, (dynamic) => CommentContentDTO.fromJson(dynamic)));
}
//BaseEntrty<int>> 示例
static Future<BaseEntrty<int>> delete(data) async {
Response<dynamic> response = await HttpRequest.instance
.delete('/url', data);
return BaseEntrty<int>.fromJson(response.data, (dynamic) => dynamic as int);
}
//BaseEntrty<T>> 示例
static Future<BaseEntrty<StatisticResult>> simple(
Map<String, dynamic> params) async {
Response<dynamic> response = await HttpRequest.instance
.get('/url', queryParameters: params);
return BaseEntrty<StatisticResult>.fromJson(
response.data, (dynamic) => StatisticResult.fromJson(dynamic));
}
//复杂实体示例,将数据解析的方法,抛出去
static Future<BaseEntrty<Map<String, PageList<CommentContentDTO>>>>
searchPreview(Map<String, dynamic> params,
{Function(dynamic dynamic) beanCreator}) async {
Response<dynamic> response = await HttpRequest.instance.get(
'/v5.0/pb/comment-info/action/search-preview',
queryParameters: params);
return BaseEntrty<Map<String, PageList<CommentContentDTO>>>.fromJson(
response.data, (dynamic) => beanCreator(dynamic));
}
}
//生成的实体示例
class StatisticResult {
/*
description: 评论数
type : integer
*/
int commentCount;
/*
description: 自定义打点统计数
type : object
*/
Object customMapper;
/*
description: 收藏数
type : integer
*/
int favoriteCount;
/*
description: 完成数
type : integer
*/
int finishCount;
/*
description: 关注数
type : integer
*/
int followCount;
/*
description: 参加数
type : integer
*/
int joinCount;
/*
description: 点赞数
type : integer
*/
int likeCount;
/*
description: 分享数
type : integer
*/
int shareCount;
/*
description: 目标资源唯一标识
type : string
*/
String targetKid;
/*
description: 目标资源类型
type : string
*/
String targetType;
/*
description: 浏览数
type : integer
*/
int viewCount;
StatisticResult();
StatisticResult.fromJson(Map<dynamic, dynamic> dynamic) {
this.commentCount = dynamic['commentCount'];
this.customMapper = dynamic['customMapper'];
this.favoriteCount = dynamic['favoriteCount'];
this.finishCount = dynamic['finishCount'];
this.followCount = dynamic['followCount'];
this.joinCount = dynamic['joinCount'];
this.likeCount = dynamic['likeCount'];
this.shareCount = dynamic['shareCount'];
this.targetKid = dynamic['targetKid'];
this.targetType = dynamic['targetType'];
this.viewCount = dynamic['viewCount'];
}
}
}
flutter 基于GeneratorForAnnotation 自动生成代码,就是这个思路,我的数据解析的方法不够好,如果,有更好的数据解析的方法,希望学习。