一个 Flutter 库,用于更轻松地创建 Rest API 客户端。灵感来自 Java Feing。
功能
- 通过通用接口方便 JSON 编码和解码。
- 方便的 HTTP 错误处理。
- 方便的 HTTP 头控制。
- 动态生成带有路径和查询参数的 URL。
- HTTP 错误时可配置的重试次数。
入门
只需添加包并遵循上述说明
dependencies:
hermes_http: ^1.0.0
用法
创建一个客户端类,并在构造函数中提供默认配置和请求模板;
class FruitClient {
late HermesHttpClient _client;
FruitClient() {
//Creates a http client with base url and the common headers
_client = HermesHttpClient("https://www.fruityvice.com/");
_client.addHeader("Content-Type", "application/json");
_client.addHeader("Connection", "keep-alive");
}
}
然后创建请求和响应的数据类,并为每个类提供一个 JsonDecoder、JsonEncoder 接口的实现。
//classes that implements json parser and json encoder,
//its not mandatory parser and encoder interfaces are implemented by the data class itself.
//the interface can be implemented by another classes
class Fruit implements JsonDecoder<Fruit>, JsonEncoder<Fruit> {
String genus = "";
String name = "";
int id = 0;
String family = "";
String order = "";
Nutrition nutritions = Nutrition();
@override
Fruit fromJson(dynamic jsonMap) {
Fruit fruit = Fruit();
fruit.genus = jsonMap['genus'];
fruit.name = jsonMap['name'];
fruit.id = jsonMap['id'];
fruit.family = jsonMap['family'];
fruit.order = jsonMap['order'];
fruit.nutritions = NutritionDecoder().fromJson(jsonMap['nutritions']);
return fruit;
}
@override
Map<String, dynamic> toJson(Fruit obj) {
Map<String, dynamic> map = <String, dynamic>{};
map['genus'] = obj.genus;
map['name'] = obj.name;
map['id'] = obj.id;
map['family'] = obj.family;
map['order'] = obj.order;
map['nutritions'] = NutritionEncoder().toJson(obj.nutritions);
return map;
}
}
class Nutrition {
num carbohydrates = 0;
num protein = 0;
num fat = 0;
num calories = 0;
num sugar = 0;
}
class NutritionDecoder extends JsonDecoder<Nutrition> {
@override
Nutrition fromJson(jsonMap) {
Nutrition nutrition = Nutrition();
nutrition.carbohydrates = jsonMap['carbohydrates'];
return nutrition;
}
}
class NutritionEncoder extends JsonEncoder<Nutrition> {
@override
Map<String, dynamic> toJson(Nutrition obj) {
Map<String, dynamic> map = <String, dynamic>{};
map['carbohydrates'] = obj.carbohydrates;
return map;
}
}
使用数据类创建请求模板。
为每个请求模板提供一个 HermesRequest<Request, Response> 引用,请求或响应对象都可以是 void(如果您想忽略 void 响应,只需将其传递给 void)。
然后,在构造函数中实例化请求模板
参数是
- hermes http 客户端(或 IHermesHttpClient 的自定义实现)
- http 方法小写
- 带有方括号内任何参数的路径( /api/fruit/{fruitName} , /api/fruit/nutrition?min={minumunValue}&max={maximumValue} )
- JsonEncoder 接口的实现(对于 void 值使用 VoidJsonEncoder())
- JsonDecoder 接口的实现(对于 void 值使用 VoidJsonDecoder())
- 可选命名参数 maxAttempts 用于配置重试(默认为 3)
- 可选的自定义请求头( Map<String,String> )
class FruitClient {
late HermesHttpClient _client;
//declares a request with the request body type and the response body type
late HermesRequest<void, Fruit> _getFruit;
late HermesRequest<void, List<Fruit>> _getAllFruit;
late HermesRequest<Fruit, void> _addFruit;
FruitClient() {
//Creates a http client with base url and the common headers
_client = HermesHttpClient("https://www.fruityvice.com/");
_client.addHeader("Content-Type", "application/json");
_client.addHeader("Connection", "keep-alive");
//Creates the request instance
_getFruit = HermesRequest(_client, 'get', '/api/fruit/{fruitName}', VoidJsonEncoder(), Fruit());
//Create the request using the class ListJsonDecoder to parse the json list
_getAllFruit = HermesRequest(_client, 'get', '/api/fruit/all', VoidJsonEncoder(), ListJsonDecoder<Fruit>(Fruit()));
//Create the request setting the maxAttemps (retry) to only 1 (defaults 3)
_addFruit = HermesRequest(_client, 'put', '/api/fruit', Fruit(), VoidJsonDecoder(), maxAttempts: 1);
}
}
然后只需完成客户端方法的公开
class FruitClient {
late HermesHttpClient _client;
//declares a request with the request body type and the response body type
late HermesRequest<void, Fruit> _getFruit;
late HermesRequest<void, List<Fruit>> _getAllFruit;
late HermesRequest<Fruit, void> _addFruit;
FruitClient() {
//Creates a http client with base url and the common headers
_client = HermesHttpClient("https://www.fruityvice.com/");
_client.addHeader("Content-Type", "application/json");
_client.addHeader("Connection", "keep-alive");
//Creates the request instance
_getFruit = HermesRequest(_client, 'get', '/api/fruit/{fruitName}', VoidJsonEncoder(), Fruit());
//Create the request using the class ListJsonDecoder to parse the json list
_getAllFruit = HermesRequest(_client, 'get', '/api/fruit/all', VoidJsonEncoder(), ListJsonDecoder<Fruit>(Fruit()));
//Create the request setting the maxAttemps (retry) to only 1 (defaults 3)
_addFruit = HermesRequest(_client, 'put', '/api/fruit', Fruit(), VoidJsonDecoder(), maxAttempts: 1);
}
//Creates a call to request
//Pass any kind of param (path, query) in the path as a map
Future<Fruit> getFruit(String fruitName) async {
return await _getFruit.call(pathParams: { 'fruitName': fruitName });
}
Future<List<Fruit>> getAllFruit() async {
_getAllFruit.addHeader("Accept", "application/json"); //dinamically set a header to the request
return await _getAllFruit.call();
}
Future<void> addFruit(Fruit fruit) async {
await _addFruit.call(body: fruit);
}
}
完整的示例可以在 github 的 examples 文件夹中找到。
如果 http 调用返回的 http 状态码不是 2xx 系列,将抛出以下异常
class HermesRequestError implements Exception {
int status = 0;
String body = "";
String uri = "";
String method = "";
HermesRequestError(this.status, this.method, this.uri, this.body);
}
附加信息
欢迎贡献。