Responses
Build an explicit response when the status, content type, or headers matter:
Route.post('/posts', (Request request) async { final post = await posts.create(request.all()); return Response.json(post, status: 201);});For simple handlers, Maat can also turn common Dart values into responses for you.
Returning Responses
Every route returns something, and Maat normalises it:
Route.get('/a', (Request request) => '<h1>Hello</h1>'); // 200 text/htmlRoute.get('/b', (Request request) => {'ok': true}); // 200 application/jsonRoute.get('/c', (Request request) => [1, 2, 3]); // 200 application/jsonRoute.get('/d', (Request request) => null); // 204 No ContentRoute.get('/e', (Request request) => Response.json({})); // exactly what you builtMaps, lists, and objects with toJson() become JSON. Strings become HTML,
null becomes 204, and a Response passes through untouched. Other values
use their toString() representation as plain text. Use Response.text() when
a string must be served as text/plain.
Building Responses
Response.json({'id': 1}); // 200, application/jsonResponse.json({'id': 1}, status: 201);Response.text('pong'); // text/plainResponse.html('<h1>Hi</h1>'); // text/htmlResponse.redirect('/login'); // 302 with a location headerResponse.redirect('/login', status: 301);Response.noContent(); // 204Stream a large or incremental body without collecting it in memory:
Response.stream(file.openRead(), headers: { 'content-type': 'application/octet-stream',});PublicFiles uses this path for files under public/.
Prefer a named constructor because it makes the intended content type clear. A bare
Response(...)holding a map or list is JSON-encoded when sent and receives a JSON content type if you did not provide one, butResponse.jsonmakes that contract explicit at construction time.
Attaching Headers
Responses are immutable. Each of these returns a new response rather than mutating the original:
Response.json(post) .header('x-request-id', id) .withHeaders({'cache-control': 'no-store'}) .status(201);Header names are lower-cased, so Content-Type and content-type are the same header. A header you pass explicitly wins over the default one a constructor would set.
Header values are part of the HTTP response boundary. Validate untrusted values
before using them in headers such as location or content-disposition.
Inspecting a Response
response.statusCode; // intresponse.headers; // unmodifiable, lower-cased keysresponse.body; // the stored String, bytes, stream, or other valueJSON Encoding
Response.json encodes with Response.encodeJson, which understands any object exposing a toJson() method:
class Post { Post(this.id, this.title); final int id; final String title;
Map<String, dynamic> toJson() => {'id': id, 'title': title};}
Route.get('/posts/{id}', (Request request, String id) => Response.json(Post(1, 'Hello')));// {"id":1,"title":"Hello"}DateTime values are encoded as ISO-8601 strings. Unsupported values passed
directly to Response.json throw a JSON encoding error; when an unsupported
value is returned from a route, normalisation uses its string form instead.
Redirects
Redirect responses default to 302 and set the location header:
return Response.redirect('/login');return Response.redirect(route('posts.show', {'id': post.id}), status: 303);Use a local path or a destination you trust. If a request parameter controls the target without validation, the endpoint becomes an open redirect.
Aborting
Throw an HTTP exception from anywhere — a handler, a middleware, a service deep in your application — and the exception handler renders it:
abort(404);abort(403, 'You do not own this post.');abort(429, 'Slow down.', {'retry-after': '30'});The typed exceptions are equivalent and read better where the status is fixed:
throw NotFoundHttpException();throw UnauthorizedHttpException('Token expired.');throw ForbiddenHttpException('This action is unauthorized.');throw TooManyRequestsHttpException();Each renders with its status and a {"message": "..."} body, and carries any headers it was given. Omit the message and you get the standard reason phrase for the status.
To return a specific response from deep in a call stack, throw it:
throw HttpResponseException(Response.json({'reason': 'locked'}, status: 423));That response is returned exactly as built, and is never logged as an error.
See Error Handling for how these become responses, and how to customise the rendering.