HTTP REST
HTTP REST provides all the necessary tools to simplify and streamline your HTTP interactions. It is a lightweight and vercetile networking library based on the popular Flutter's http library and serves as an enhancement, providing additional features and functionalities such as
- Middlewares (Interceptors)
- Request/Response body converters
- Request/Response logger
- Read/Write progress tracking
- Multipart requests with progress
Getting Started
Create the instance of HttpRestClient
, then run the HttpRestReqeust
.
import 'package:http/http.dart' as http;
import 'package:http_rest/http_rest.dart';
/// ...
final httpClient =
HttpRestClient.builder(DefaultRequestExecutor(http.Client()))
.addResponseConverter(JsonToMapResponseConverter()) // Request converter
.addRequestConverter(MapToJsonRequestConverter()) // Response converter
.addRequestMiddleware(RequestLogger()) // Request Middleware
.addResponseMiddleware(ResponseLogger()) // Response Middleware
.build();
// This request will add a book to library
final result = await httpClient.execute(HttpRestRequest(
method: Methods.post,
// Specifies request converter type
requestConverterType: MapToJsonRequestConverter,
// Specifies response converter type
responseConverterType: JsonToMapResponseConverter,
url: 'https://example.com/books',
headers: {'Language': 'en'},
body: {
"id":2,
"bookName":"1984",
"author":"George Orwell"
}));
if(result.rowResponse.code == 201){
print(result.response); // instance of Map
}
...
The same httpClient
instance can be used to run other HTTP requests as well.
// The request will get the library books.
final result = await httpClient.execute(HttpRestRequest(
method: Methods.get,
responseConverterType: JsonToMapResponseConverter,
url: 'https://example.com/books?count='$10''))
Middlewares
HttpRestClient uses middleware chains to modify requests and responses.
Here is how to create a request middleware that adds an authorization header to each HttpRestRequest
.
class AuthorizationMiddleware extends Middleware<RowRequest> {
@override
Future<RowRequest> onNext(
RowRequest row, Middleware<RowRequest> nextMiddleware) async {
row.request.headers['Authorization'] = 'YOUR AUTHORIZATION TOKEN';
return await super.onNext(row, nextMiddleware);
}
}
And add it while building the instance of HttpRestClient
as shown below
final httpClient = HttpRestClient.builder(
DefaultRequestExecutor(http.Client()))
.addResponseConverter(JsonToMapResponseConverter())
.addRequestConverter(MapToJsonRequestConverter())
.addRequestMiddleware(AuthorizationMiddleware()) // Added
.addRequestMiddleware(RequestLogger())
.addResponseMiddleware(ResponseLogger())
.build();
Hence, the 'Authorization' header will be added to every request that has been executed by the httpClient
.
Note that RequestLogger
and ResponseLogger
are also middlewares.
Any number of request and response Middlewares can be added to HttpRestClient
, and they will be called as a chain in the same order as has been added.
Converters
Converters are used to convert request and response bodies. Library ships with a few default converters
MapToJsonRequestConverter
used to convert request's map body to JSON string.
JsonToMapResponseConverter
used to convert response body bytes to map object.
StringResponseConverter
used to convert response body bytes to String.
Each HttpRestRequest
can specify the request and response converter type, and HttpRestClient
will use specified converters to convert the request and response bodies.
// The request will get the library books.
final result = await httpClient.execute(HttpRestRequest(
method: Methods.get,
// Converts Request body to JSON
responseConverterType: JsonToMapResponseConverter,
url: 'https://example.com/books?count='$10''))
Here is how to create a converter that converts received body bytes to map instance.
import 'dart:convert';
class JsonToMapResponseConverter extends ResponseConverter {
@override
HttpRestResponse fromRow(RowResponse rowResponse) {
dynamic jsonMap;
final rowBody = rowResponse.bodyBytes;
if (rowBody != null && rowBody.isNotEmpty) {
final rowBodyUtf8 = utf8.decode(rowBody);
jsonMap = json.decode(rowBodyUtf8);
}
return HttpRestResponse(rowResponse.request, rowResponse, jsonMap);
}
}
Notice that fromRow
method receives instance of RowResponse
and returns instance of HttpRestResponse
.
RowResponse
is lower level of response model, it containsbodyBytes
of response and more, like response code and response headers.HttpRestResponse
is whatawait client.execute(HttpRestRequest(...))
returns, it contains instance of originalHttpRestRequest
,RowResponse
and converted bodyjsonMap
.
Request Logging
RequestLogger
and ResponseLogger
are used to log the network interactions in the console.
Here is how logged request looks like in the console.
→ REQUEST →
POST: https://example.com/books
HEADERS: Content-Type : application/json
Authorization : eyJhbGciOi....
Language : EN
Version : 1.1.76
BODY: {"id":2, "bookName":"1984", "author":"George Orwell"}
← RESPONSE ←
POST: https://example.com/books
CODE: 200
HEADERS: connection : keep-alive
date : Thu, 04 May 2023 19:02:10 GMT
transfer-encoding : chunked
vary : accept-encoding
content-encoding : gzip
strict-transport-security : max-age=15724800; includeSubDomains
content-type : application/json
BODY: {"message":"Success"}
Control loaggable parts of the request with LogParts
enum
enum LogParts {
headers,
body,
url,
code;
static const all = {url, headers, code, body}; // default
}
By default, all the parts of the request and response are logged. Here is how to specify Logger to log only URL and headers.
final httpClient = HttpRestClient.builder(
DefaultRequestExecutor(http.Client()))
//...
.addRequestMiddleware(RequestLogger(logParts: {LogParts.url,LogParts.headers})) // Middlewares
.addResponseMiddleware(ResponseLogger(logParts: {LogParts.url,LogParts.headers}))
.build();
Read/Write Progress Tracking
To track the read/write progress of any request use writeProgressListener
and readProgressListener
members of HttpRestRequest
.
Here is an example of tracking image download progress from GitHub.
void downloadImage() =>
httpClient.execute(HttpRestRequest(
method: Methods.get,
url: 'https://raw.githubusercontent.com/RobertApikyan/http_rest/main/doc/assets/intro.png',
readProgressListener: (bytes, totalBytes) => print('download progress= ${bytes/totalBytes}'),
));
Multipart Request
For the multipart request just provide a MultipartRequestBody
to HttpRestRequest
's body.
Here is how to create a multipart request to upload a book and watch the progress.
void uploadBook(MultipartFile multipartFile) =>
httpClient.execute(HttpRestRequest(
method: Methods.post,
url: 'https://example.come/book',
body: MultipartRequestBody(
fields: {},
files: [multipartFile],
progressListener: (bytes, totalBytes) {
// watch the progress
final progress = bytes / totalBytes
},
),
));
Request Executor
The RequestExecutor
is responible for running actual http requests and return the result. It's an abstract class, with a single abstract method execute
abstract class RequestExecutor {
/// Override this method and implement http call by using the parameters from
/// the [rowRequest].
Future<RowResponse> execute(RowRequest rowRequest);
}
DefaultRequestExecutor
is a default implementation of RequestExecutor
and uses http library for network interactions.
HttpRestClient
The HttpRestClient
class acts as the central hub, coordinating the flow of requests and responses, and allowing for extensibility and customization at various stages through request and response converters, middlewares, and the RequestExecutor class.
When a request is made through the HttpRestClient, the following steps occur:
-
Request Override Middleware: The request is streamed through the provided middleware before passing to request converter, thereby let to modify each request in the initial step before passing to converter.
-
Request Conversion: The request object is passed through the request converter, which transforms it into the appropriate format for sending over the network. This ensures compatibility with the API endpoint and handles any necessary data conversions.
-
Request Middlewares: The converted request then goes through a chain of request middlewares. These middlewares allow you to inject custom logic before the request is sent. Examples of request middleware functionalities include authentication, adding headers, or modifying the request payload.
-
Request Execution: The processed request is passed to the RequestExecutor class, which handles the actual execution of the HTTP request. The RequestExecutor interacts with the network layer, communicates with the API endpoint, and receives the raw response.
-
Response Middlewares: The response received from the RequestExecutor is then passed through a chain of response middlewares. These middlewares enable you to manipulate and process the response before it is returned to the caller. Common use cases for response middlewares include parsing response data, error handling, or logging.
-
Response Conversion: After going through the response middlewares, the response is passed to the response converter. The response converter transforms the raw response into a structured format that aligns with your application's needs. This conversion step ensures that the response is in a format that can be easily consumed and understood by your code.
-
Result Return: Finally, the converted response is returned as the result of the original request made through the HttpRestClient. The caller receives the processed response, which can be further processed or used to update the application's state.
Conclusion
In summary, the HTTP REST Library simplifies and enhances RESTful API integration. With support for RESTful methods, middlewares, converters, and multipart requests, it streamlines HTTP interactions. It provides customization options, abstraction, error handling, and enhanced logging, making it a valuable tool for building robust applications.
Please fill free to ask a question or open an issue in the github I will be happy to answer.