Flutter插件lemonade的使用_lemonade 是一个简单而强大的数据验证库
Flutter插件lemonade的使用_lemonade 是一个简单而强大的数据验证库
lemonade
是一个简单而强大的数据验证库。它可以用于标准数据结构以及任何自定义数据。该库具有以下特点:
- 通用性:可以用于标准数据结构和任何自定义数据。
- 可扩展性:可以轻松扩展以处理任何同步验证用例。
- 安全性:防止由于拼写错误或空值导致的运行时错误。
- 测试覆盖率超过80%。
- 纯Dart编写。
- 零依赖。
主要灵感来源是JSON Schema和ajv
库。
数据验证是什么?
验证是一种确保应用程序使用的数据正确且安全的方法。这意味着对象的所有属性都已指定,并且符合预期。
例如,JSON数据验证:
// 正确的JSON
{
"name": "John",
"age": 25,
"email": "john@example.com"
}
// 错误的JSON
{
"name": "John",
"age": "25", // 字符串而不是数字
"email": "john@example.com"
}
如果你为这样的对象编写验证器,你可以100%确定数据会像你期望的那样。
使用方法
例如,你想验证一个GeoJSON点对象,其格式如下:
{
"type": "Point",
"coordinates": [125.6, 10.1]
}
验证器将如下所示:
final pointsValidator = Validator.object(
items: {
// "type": "Point"
'type': Validator.equals('Point'),
// "coordinates": [125.6, 10.1]
'coordinates': Validator.list(
item: Validator.number(),
// 长度正好为2
minItems: 2,
maxItems: 2,
),
},
);
然后,你可以使用非常基础的方法检查任何数据:
final decodedData = jsonDecode('{"type":"Point","coordinates":[125.6,10.1]}');
print(pointsValidator.validate(decodedData)); // true
如果想检查具体哪里出错,可以使用getError
方法:
final wrongData = jsonDecode('{"type":"Point","coordinates":[125.6,"10.1"]}');
print(pointsValidator.getError(wrongData)); // object.coordinates > list<number>[1] > expected(number).got("10.1")
字符串形式的错误信息表示:
- 在字段
"coordinates"
- 在索引
[1]
- 期望任意数字
- 得到
"10.1"
- 在索引
这样很容易解码并追踪数据中的错误,不是吗?
常见模式
大多数情况下,你会遇到一些常见的数据类型:UUID、IP地址、十六进制字符串等。你可以使用此库轻松验证这些数据!
例如,使用Validators
类验证UUID:
final stringToCheck = 'd87955f3-42a8-43db-aea2-78c0e29e5d23';
Validators.uuid().validate(stringToCheck);
它也可以在复杂的嵌套验证器中轻松使用:
final structureToCheck = {
'ip': '127.0.0.1',
'mac': '0C:D3:86:34:34:66',
};
final validator = Validator.object(
items: {
'ip': Validators.ipv4(),
'mac': Validators.mac(),
},
);
validator.validate(structureToCheck);
自定义验证器
有时你需要验证某些自定义数据结构(例如自己的类)。lemonade
提供了两种方法来实现这一点:
- 创建通用、可重用的验证器类。
- 使用
Validator.customValue
工厂。
通用验证器类
lemonade
导出了几个抽象类,你可以继承它们以实现自定义验证器:
Validator
ValueValidator
CollectionValidator
CompoundValidator
验证工作方式类似:
getError
方法必须覆盖,如果返回null
,则数据有效;如果返回ValidationError
,则数据无效。- 必须指定
annotation
,以便将验证器字符串化并查看其验证的内容。
ValidationError
有两个必需字段:expected
和 actual
(类似于dart测试)。
在actual
中,你应该指定被检查的值。
在expected
中,你应该指定有效的数据标准。
例如,你想验证数字是否为偶数。首先,你必须检查数据是否为数字,然后检查偶数性。实现如下:
class EvennessValidator extends Validator {
EvennessValidator() : super(annotation: 'even number');
@override
ValidationError? getError(dynamic data) {
if (data is! int) {
// 我们期望数字,但得到了其他东西
return ValidationError(expected: 'number', actual: data);
}
if (!data.isEven) {
// 我们期望偶数,但得到了奇数
return ValidationError(expected: 'even number', actual: data);
}
// 如果一切正常,返回null
return null;
}
}
然后你可以这样使用它:
// 创建一个新的验证器实例
final validator = EvennessValidator();
// 使用默认方法
validator.validate(14);
这种方法的主要优点:
- 验证器看起来和工作方式与默认的一样。
- 验证错误更具描述性和可定制性。
- 验证器可以轻松重用。
但是也有几个缺点:
- 需要更多的定制和整体代码。
- 对于一次性使用的验证器来说不够方便。
Validator.customValue
如果你只是想完成任务,不需要理解具体的无效原因,并且这个验证器只会被使用一次,那么使用简单的Validator.customValue
工厂会更容易。
对于检查数字是否为偶数的验证器,代码如下:
// 创建一个新的验证器实例
final validator = Validator.customValue((data) {
if (data is! int) return false;
if (!data.isEven) return false;
return true;
});
// 使用默认方法
validator.validate(14);
这很简单!这种方法的优点:
- 编写的代码更少。
- 不需要定制、调优、描述。
- 可以使用与
Iterable
中的where
方法相同的回调。
但是也有一些缺点:
- 不容易重用。
- 导致代码一致性较差。
功能和路线图
我开始这个项目是因为需要编写大量JSON验证器。后来我尝试使其更加通用,以便用于其他用例。
- ✅ 基本类型的验证器
- ✅ 常见数据类型的内置验证器
- ✅ 自定义数据结构的内置验证器
- ❌ 与JSON Schema完全向后兼容
- ❌ 客户端自动HTTP响应验证
- ❌ 服务器端自动HTTP请求验证
示例代码
以下是使用lemonade
验证Rick and Morty API
数据的完整示例:
// 忽略警告:prefer_const_constructors, avoid_print, avoid_dynamic_calls
import 'dart:convert';
import 'package:lemonade/lemonade.dart';
/// 遵循这里模式的验证器
/// https://rickandmortyapi.com/documentation/#get-all-characters
final validator = Validator.object(
items: {
'info': Validator.object(
items: {
'count': Validator.integer(min: 0),
'pages': Validator.integer(min: 0),
'next': Validator.string(),
'prev': Validator.string().nullable(),
},
),
'results': Validator.list(
item: Validator.object(
items: {
'id': Validator.integer(min: 1),
'name': Validator.string(minLength: 1),
'status': Validator.equals('Alive') |
Validator.equals('Dead') |
Validator.equals('Unknown'),
'species': Validator.string(minLength: 1),
'type': Validator.string(),
'gender': Validator.equals('Female') |
Validator.equals('Male') |
Validator.equals('Genderless') |
Validator.equals('unknown'),
'origin': locationValidator,
'location': locationValidator,
'image': Validator.string(),
'episode': Validator.list(
item: Validator.string(),
minItems: 1,
),
'url': Validator.string(),
'created': Validator.string(),
},
),
),
},
);
/// 遵循这里模式的验证器
/// https://rickandmortyapi.com/documentation/#location-schema
final locationValidator = Validator.object(
items: {
'name': Validator.string(minLength: 1),
'url': Validator.string(),
},
);
/// 应该验证的示例数据
const exampleJson = '''
{
"info": {
"count": 826,
"pages": 42,
"next": "https://rickandmortyapi.com/api/character/?page=2",
"prev": null
},
"results": [
{
"id": 1,
"name": "Rick Sanchez",
"status": "Alive",
"species": "Human",
"type": "",
"gender": "Male",
"origin": {
"name": "Earth",
"url": "https://rickandmortyapi.com/api/location/1"
},
"location": {
"name": "Earth",
"url": "https://rickandmortyapi.com/api/location/20"
},
"image": "https://rickandmortyapi.com/api/character/avatar/1.jpeg",
"episode": [
"https://rickandmortyapi.com/api/episode/1",
"https://rickandmortyapi.com/api/episode/2"
],
"url": "https://rickandmortyapi.com/api/character/1",
"created": "2017-11-04T18:48:46.250Z"
}
]
}
''';
void main() {
final parsedJson = jsonDecode(exampleJson);
// 这里一切都应该没问题
final isValid = validator.validate(parsedJson);
if (isValid) {
print('Everything is fine');
} else {
print('Validation error!');
}
print('------------------');
// 让我们破坏这个完美的模式
parsedJson['results'][0]['type'] = 999;
// 现在这里有一个错误
final errors = validator.getError(parsedJson);
print('Errors: $errors');
}
更多关于Flutter插件lemonade的使用_lemonade 是一个简单而强大的数据验证库的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter插件lemonade的使用_lemonade 是一个简单而强大的数据验证库的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在探索Flutter中未知功能插件(如lemonade)的潜在用途时,直接给出代码案例可能有些困难,因为“lemonade”并不是一个广为人知的Flutter插件名称。不过,我可以基于一般的Flutter插件开发和使用流程,给出一个假设性的代码案例,用以展示如何集成和探索一个假设的Flutter插件。
假设lemonade插件提供了一个用于数据收集和分析的功能,我们可以编写以下Flutter代码来集成并探索其潜在用途。
1. 添加依赖
首先,我们需要在pubspec.yaml
文件中添加对lemonade插件的依赖(注意:这里的lemonade是假设的,实际使用时需要替换为真实插件名):
dependencies:
flutter:
sdk: flutter
lemonade: ^x.y.z # 假设的版本号
2. 导入插件
在需要使用lemonade插件的Dart文件中导入它:
import 'package:lemonade/lemonade.dart';
3. 初始化插件
在Flutter应用的入口点(通常是main.dart
)中初始化插件:
void main() {
WidgetsFlutterBinding.ensureInitialized();
// 假设lemonade有一个初始化方法
Lemonade.instance.initialize();
runApp(MyApp());
}
4. 使用插件功能
假设lemonade插件提供了数据收集和分析的功能,我们可以在应用的某个页面中使用它:
import 'package:flutter/material.dart';
import 'package:lemonade/lemonade.dart';
class DataCollectionPage extends StatefulWidget {
@override
_DataCollectionPageState createState() => _DataCollectionPageState();
}
class _DataCollectionPageState extends State<DataCollectionPage> {
String analysisResult = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Data Collection with Lemonade'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Analysis Result:',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 16),
Text(
analysisResult,
style: TextStyle(fontSize: 18),
),
SizedBox(height: 32),
ElevatedButton(
onPressed: () async {
// 假设lemonade有一个collectData方法进行数据收集
// 和一个analyzeData方法进行数据分析
List<Map<String, dynamic>> data = await Lemonade.instance.collectData();
String result = await Lemonade.instance.analyzeData(data);
setState(() {
analysisResult = result;
});
},
child: Text('Collect and Analyze Data'),
),
],
),
),
);
}
}
5. 运行应用
完成上述步骤后,运行Flutter应用,你应该能够在指定的页面上看到一个按钮,点击该按钮将触发数据收集和分析操作,并在页面上显示分析结果。
注意事项
- 上述代码是基于假设的lemonade插件功能编写的,实际使用时需要根据真实插件的文档进行调整。
- 如果lemonade插件不存在或功能不符,你可能需要寻找其他类似的Flutter插件或自行开发所需功能。
- 在集成和使用任何第三方插件时,务必阅读其官方文档,了解插件的API、使用方法和注意事项。