Response
This page shows what a controller can return, and how to set the status, headers and cookies.
Return a value
Return the value from your controller. Dox turns it into the response.
| You return | The client gets |
|---|---|
a String |
the text |
a Map or a List |
JSON |
| a model, a list of models, a serializer | JSON (from toJson()) |
a DateTime |
the ISO 8601 text |
an IHttpException, like NotFoundHttpException() |
its status code and JSON: {"code": ..., "message": ...}, or a Map message as it is |
a File (from dart:io) |
the file, streamed |
a Stream<List<int>> |
the bytes, streamed |
StreamFile, DownloadableFile (also from Storage() of dox_storage) |
the file, streamed |
response(...) |
full control: status, headers, cookies |
// JSON
Map<String, String> hello(DoxRequest req) {
return <String, String>{'message': 'hello world!'};
}
// a model
Future<Blog?> show(int id) async {
return Blog().find(id);
}
Errors
Throw or return an exception to answer with an error.
| Exception | Status |
|---|---|
BadRequestException() |
400 |
NotFoundHttpException() |
404 |
PayloadTooLargeException() |
413 |
UnsupportedMediaTypeException() |
415 |
TokenMismatchException() |
419 |
ValidationException() |
422 |
TooManyRequestsException() |
429 |
InternalErrorException() |
500 |
Each one takes message:, errorCode: and code::
The client gets 404 with {"code":"not_found","message":"Blog not found"}.
code is the errorCode. A Map message is sent as it is.
For another status, extend IHttpException:
import 'dart:io';
import 'package:dox_core/dox_core.dart';
class ForbiddenException extends IHttpException {
ForbiddenException({String message = 'Forbidden'}) {
super.code = HttpStatus.forbidden;
super.errorCode = 'forbidden';
super.message = message;
}
}
Any other error gets 500. It is also passed to the exception handler.
Files and big responses
Return a File or a Stream<List<int>>. Dox sends it in chunks. It does not load it all into memory.
- A
FilesetsContent-Type(from the file name),Content-LengthandAccept-Ranges. - It answers
Range: bytes=...with206 Partial Content. Video players can seek, and downloads can resume. Storage().stream()andStorage().download()on the local disk also stream the file.
import 'dart:io';
import 'package:dox_storage/dox_storage.dart';
// a file from disk
Route.get('/video', (DoxRequest req) => File('storage/video.mp4'));
// a download from Storage
Future<DownloadableFile> download(DoxRequest req) async {
// sends Content-Disposition, so the browser saves the file
return Storage().download('report.pdf');
}
// a stream from Storage
Future<StreamFile> show(DoxRequest req) async {
return Storage().stream('photo.jpg');
}
See File Storage.
The response() helper
Use response(content) when you need to set the status, headers or cookies.
All methods return the response, so you can chain them.
import 'dart:io';
import 'package:dox_core/dox_core.dart';
DoxResponse store(DoxRequest req) {
return response(<String, String>{'status': 'created'})
.statusCode(201)
.header('X-Request-Id', 'abc')
.contentType(ContentType.json);
}
| Method | Does |
|---|---|
statusCode(201) |
sets the status code. Default 200. |
header('X-Key', 'value') |
sets one header |
withHeaders({...}) |
adds these headers. A key replaces the header with the same name, in any case. Other headers stay. |
contentType(ContentType.html) |
sets Content-Type (ContentType is from dart:io) |
cache(Duration(minutes: 5)) |
sets Cache-Control: max-age=300 |
cookie(DoxCookie(...)) |
sets a cookie |
content(value) |
changes the body |
stream(byteStream) |
sends a Stream<List<int>> as the body |
Redirect
// 302 to a path
return redirect('/login');
// another status
return redirect('/posts', status: 301);
// back to the page the request came from (Referer), or '/'
return back(req);
See Session for flash messages and errors with a redirect.
Cookie
// set a cookie
DoxCookie cookie = DoxCookie('theme', 'dark');
return response('ok').cookie(cookie);
// set a cookie with options
DoxCookie token = DoxCookie(
'token',
'value',
maxAge: Duration(days: 7), // sent as Max-Age in seconds. default 1 hour
httpOnly: true, // default true: page scripts can not read it
secure: true, // default: Secure only when the request came over https
path: '/',
sameSite: SameSite.lax, // from dart:io. default: none sent
);
return response('ok').cookie(token);
// delete a cookie
return response('ok').cookie(DoxCookie('token', ''), setExpire: true);
To read a cookie, see Request: cookies.
Encrypt and decrypt
By default, Dox encrypts the cookie value with APP_KEY, and decrypts it when you read it.
To turn this off, use DoxCookie(key, value, encrypt: false) to set it,
and req.cookie(key, decrypt: false) to read it.
Change every response
To change every response in one place (for example, to add a header), set a responseHandler in AppConfig.