The body of the request is the data that is sent by the client to the server. The body can be in many different formats, such as JSON or plain text. The body is read-only, meaning that you cannot modify the body of the request.
When the request is received by the server, the body's type is a Stream of bytes. Streams are a way to read data in chunks, which is useful for reading large files or data that is not fully available at once. Most of the time, you will not need to read the body as a stream, and you can read the body as a string or JSON object.
Determining the Body Type#
As the developer, you get to decide how you expect the body to be formatted. The body can be in many different formats, such as JSON, plain text, or binary data. You can determine whether you want to support multiple body types or only one.
Reading the Body#
The request's body is lazy-loaded, meaning that the body is not read/resolved until you access it. This is useful because you may not need to read the body for every request, and you can save resources by not reading the body if it is not needed.
In the Endpoint Handler#
When you use the body within the your endpoint handler, Revali Server will automatically resolve the body for you. The body will be resolved as a string, JSON object, stream, etc, depending on the body's type. The value will be passed to your endpoint handler as an argument as long as the body type and the parameter type match.
@Get()
Future<void> saveUser(
// highlight-next-line
@Body() Map<String, dynamic> data,
) async {
...
}
Outside the Endpoint Handler#
If you need to read the body within a Lifecycle Component, you can access the body using the Request
object from the context.
The body needs to be resolved before you can access it. You can resolve the body by calling the resolvePayload
method on the Request object.
If you try to access the body without resolving it first, an UnresolvedPayloadException will be thrown.
class MyComponent implements Middleware {
const MyComponent();
@override
Future<void> use(MiddlewareContext context) async {
// highlight-next-line
await context.request.resolvePayload();
final body = context.request.body;
}
}
Calling resolvePayload multiple times will not re-read the body, the body will be resolved and cached in memory the first time
reservePayload is called.
There are no limits to how many times you can access the body
Resolving the payload will check the Content-Type header and resolve the body as the appropriate type.
Builtin Body Types#
String#
Content-Type: text/plain
Dart Type: StringBodyData
JSON#
Content-Type: application/json
Dart Type: JsonBodyData
Form Data#
Content-Type: application/x-www-form-urlencoded
Dart Type: FormDataBodyData
Multipart Form Data#
Content-Type: multipart/form-data
Dart Type: FormDataBodyData
Binary Data#
Content-Type: application/octet-stream
Dart Type: BinaryBodyData
Custom Body Types#
If you need to support a custom body type, you can create a class that extends the BodyData
class. Once you have created your custom body type, you'll need to register it with the PayloadImpl.
Create Body Type#
import 'dart:convert';
import 'package:revali_router/revali_router.dart';
base class MyBodyData extends BodyData {
MyBodyData(
this._bytes,
this._encoding,
this._headers,
);
final Stream<List<int>> _bytes;
final Encoding _encoding;
final ReadOnlyHeaders _headers;
@override
Stream<List<int>> get data => _bytes;
@override
ReadOnlyHeaders headers(ReadOnlyHeaders? requestHeaders) {
return requestHeaders ?? EmptyHeaders();
}
@override
bool get isNull => false;
@override
Stream<List<int>>? read() {
throw UnimplementedError();
}
@override
String? get mimeType => 'binary/octet-stream';
}
Create Body Parser#
You will also need to create a class that extends the BodyParser class. The BodyParser
class is responsible for parsing the body data and returning the body as a BodyData object.
import 'dart:convert';
import 'package:revali_router/revali_router.dart';
base class MyBodyParser extends BodyParser {
const MyBodyParser(super.mimeType);
@override
Future<BodyData> parse(
Encoding encoding,
Stream<List<int>> data,
ReadOnlyHeaders headers,
) async {
return MyBodyData(data, encoding, headers);
}
}
Register#
You can register your custom body type within the constructor of your AppConfig
import 'package:revali_router/revali_router.dart';
@App()
final class MyApp extends AppConfig {
MyApp() : super(host: 'localhost', port: 8083) {
PayloadImpl.additionalParsers['binary/octet-stream'] =
MyBodyParser('binary/octet-stream');
}
}