Request
This page shows how to read input, files, headers and cookies from a request.
Typed input (recommended)
Ask for a DTO in your controller. Dox reads the body (or the query for GET, HEAD and DELETE), checks it, and gives you a typed object.
Route.put('/users/{id}', (UserController c) => c.update);
@Inject()
class UserController {
Future<User?> update( // (1)!
int id, // (2)!
UpdateUser input, // (3)!
) async {
User? user = await User().find(id);
user?.name = input.name;
await user?.save();
return user;
}
}
- The return value is the response. Dox waits for a
Future. AStringis sent as text. AMapor a model is sent as JSON. - A path param. It is
{id}from the route./users/abcanswers404, becauseabcis not anint. - A DTO. Dox reads the body, checks it, and builds the object before this method runs.
Bad input gets 422 before your method runs. See Validation.
Raw input
Add a DoxRequest parameter to read the raw values. These values are not checked.
@Inject()
class ApiController {
String sayHello(DoxRequest req) {
String? name = req.input('name');
return 'Hello $name';
}
}
| Code | Gives |
|---|---|
req.input('name') |
one value from the query string or the body. The body wins when both have the key. |
req.input('user.name', 'guest') |
a value inside a map or list, with a dot key. The second value is the default, used when the key is not there or is null. |
req.all() |
all values from the query string and the body, as a Map |
req.only(<String>['name', 'email']) |
only these keys |
req.except(<String>['password']) |
all values, without these keys |
req.has('name') |
true when the key is there as it is and not empty. It does not read dot keys. |
req.filled('name') |
true when the key is there and not empty. A text of only spaces, an empty list and an empty map count as empty. |
req.exists('name') |
true when the key is there, with any value (also '' or null) |
req.missing('name') |
true when the key is not there |
req.boolean('remember') |
true for true, 1, on and yes. Else false. |
req.integer('page', 1) |
the value as an int. The default is used when the key is not there or is not a number. |
req.body |
the body only (JSON, form data) |
req.query |
the query string only |
req.add('key', value), req.merge({...}) |
add values, for example in a middleware |
A dot key reads inside the input. req.input('items.0.id') gives the id of the first item.
A key that is in the input as it is wins. So a body key 'a.b' is read before the b key inside a.
Dot keys also work in filled, exists, missing, boolean, integer, file and hasFile.
has does not read dot keys: req.has('user.name') is false for {"user": {"name": "Ann"}}. Use filled for that.
Form and query keys with brackets give lists and maps, like in Laravel:
| Input | req.input(...) gives |
|---|---|
tags[]=a&tags[]=b |
req.input('tags') is ['a', 'b'] |
address[city]=Yangon |
req.input('address') is {'city': 'Yangon'} |
items[0][name]=x |
req.input('items') is [{'name': 'x'}] |
name=a&name=b (no brackets) |
req.input('name') is 'b', the last value |
Route params
Ask for a path param as a typed parameter, like int id in the example above.
The text values are also in req.param, like req.param['id'].
They are URL-decoded: /users/a%20b gives 'a b'.
See Controller: parameters.
Form data (File)
An uploaded file is a RequestFile. The request must be multipart/form-data.
Put the file in a DTO, so Dox checks its type and size:
import 'package:dox_core/dox_core.dart';
part 'upload_image.dto.g.dart';
@Dto()
class UploadImage {
@Image()
@MaxSize('5MB')
final RequestFile image;
const UploadImage({required this.image});
static const DtoSchema<UploadImage> schema = $UploadImageDto;
}
@Inject()
class ApiController {
Future<Map<String, String>> uploadImage(UploadImage input) async {
String uploadedPath = await input.image.store('images');
return <String, String>{'filename': uploadedPath};
}
}
store() comes from the dox_storage package. Add it and import package:dox_storage/dox_storage.dart.
Without a DTO, req.file('image') gives the RequestFile, or null when the field is not a file.
req.hasFile('image') is true when the field holds a file or a list of files.
For a field with many files, use req.input('images'). The file is not checked.
RequestFile
RequestFile file = req.input('video');
file.filename; // name from the client
file.filetype; // content type, like video/mp4
file.extension; // mp4
file.length; // size in bytes
await file.size; // size in KB
file.stream(); // read it in chunks
await file.store('videos'); // save to storage (needs dox_storage). returns the path
await file.moveTo('/data/videos/1.mp4'); // move it. returns the new File
await file.bytes; // the whole file in memory. avoid for big files
Save the file before you return
A file up to 1 MB stays in memory. A bigger file is written to a temp file while it arrives.
The temp file is deleted when the response is sent.
So call store, moveTo or stream before your controller returns.
Size limits
A body that is too big gets 413 Payload Too Large. Dox checks Content-Length first.
It also counts the bytes while it reads the body.
AppConfig(
maxBodySize: 10 * 1024 * 1024, // JSON and urlencoded, default 10 MB
maxMultipartSize: 100 * 1024 * 1024, // form-data uploads, default 100 MB
);
Set the same limit in your proxy, like client_max_body_size in nginx.
Headers
String? token = req.header('X-Token');
Map<String, dynamic> all = req.headers; // a repeated header is joined with ', '
req.userAgent(); // User-Agent header
req.host(); // Host header
req.origin(); // Origin header, or null
req.referer(); // Referer header, or null
req.isJson(); // true for a JSON body
req.isFormData(); // true for multipart/form-data
req.locale; // first language of Accept-Language, like 'th', or null
req.bearerToken(); // the token of 'Authorization: Bearer <token>', or null
Path and URL
req.path(); // '/admin/users/5'
req.fullUrl(); // 'https://example.com/admin/users/5?tab=info'
req.pathIs('admin/*'); // true. `*` matches any text
req.routeIs('admin.users.*'); // true when the route name matches
pathIs cleans the path like the router does. A / at the start or the end does not matter, and // counts as one /. So 'admin/*' and '/admin/*' are the same, and /admin/ and /admin// match 'admin', like they reach the /admin route.
routeIs is false for a route with no name. Give a route a name with .name('admin.users.show').
Cookies
String? token = req.cookie('token'); // decrypted value
String? raw = req.cookie('theme', decrypt: false); // value as sent
See Response to set a cookie.
Client IP and scheme
Behind a proxy, req.ip() shows the proxy, not the client.
Set trustedProxies in AppConfig to fix it. See Deployment.