Request
s and Response
s. For each HTTP request an application receives, an instance of Request
is created. A Response
must be created for each request. Responses are created by controller objects. This guide discusses the behavior of request and response objects.Request
is created for each HTTP request to your application. A Request
stores everything about the HTTP request and has some additional behavior that makes reading from them easier. You handle requests by writing code in a controller object or closures.raw
property (a Dart standard library HttpRequest
). A Request
has attachments
that data can be attached to in a controller for use by a linked controller:Request
also has two built-in attachments, authorization
and path
. authorization
contains authorization information from an Authorizer
and path
has request path information from a Router
.Response
has a status code, headers and body. The default constructor takes a status code, header map and body object. There are many named constructors for common response types:Request
and Response
objects have behavior for handling the HTTP body. You decode the contents of a Request
body into Dart objects that are used in your code. You provide a Dart object to a Response
and it is automatically encoded according to the content-type of the response.Request
has a body
property. This object decodes the bytes from the request body into Dart objects. The behavior for decoding is determined by the content-type header of the request (see the section on CodecRegistry
later in this guide). When you decode a body, you can specify the Dart object type you expect it to be. If the decoded body object is not the expected type, an exception that sends a 400 Bad Request error is thrown.as
method. This method also takes a type argument to enforce the type of the decoded body object.as
or decode
if the type can be inferred. For example, object.read(await request.body.decode())
will infer the type of the decoded body as a Map<String, dynamic>
without having to provide type parameters.RequestBody
, the section on body binding for ResourceControllers and a later section in this guide on Serializable
.RequestBody.maxSize
during application initialization.GET /users/1
might be JSON object that represents a user. To ensure the client understands that the body is a JSON object, it includes the header Content-Type: application/json; charset=utf-8
.Response
that has a body, you provide a body object and a contentType
. For example:map
is first encoded as a JSON string and then to a list of UTF8 bytes.ContentType
is made up of three components: a primary type, a subtype and an optional character set.Codec
(from dart:convert
). For example, the content type application/json
selects JsonCodec
, while charset utf-8
selects Utf8Codec
. These two codecs are run in succession to convert the Map
to a list of bytes. The codec is selected by your application's CodecRegistry
; this is covered in later section.Map<String, dynamic>
can be encoded by a JsonCodec
. But if the body object cannot be encoded, a 500 Server Error response is sent. A valid input for one Codec
may not be valid for another; it is up to you to ensure that the body object is valid for the contentType
of the response.String
. It will only be converted by a charset encoder:List<int>
where each value is between 0-255).CodecRegistry
for details on built-in codecs and adding codecs.Stream<T>
. Stream<T>
body objects are most often used when serving files. This allows the contents of the file to be streamed from disk to the HTTP client without having to load the whole file into memory first. (See also FileController
.)Stream<T>
, the response will not be sent until the stream is closed. For finite streams - like those from opened filed - this happens as soon as the entire file is read. For streams that you construct yourself, you must close the stream some time after the response has been returned.ManagedObject<T>
body objects that are sent as UTF8 encoded JSON 'just works' and is suitable for most applications. When serving assets for a web application or different data formats like XML, it becomes important to understand how Conduit's codec registry works.CodecRegistry
contains mappings from content types to Codec
s. These codecs encode response bodies and decode request bodies. There are three built-in codecs for application/json
, application/x-www-form-urlencoded
and text/*
. When a response is being sent, the repository is searched for an entry that exactly matches the primary and subtype of the Response.contentType
. If an entry exists, the associated Codec
starts the conversion. For example, if the content type is application/json; charset=utf-8
, the built-in application/json
codec encodes the body object.*
) subtype, that codec is used. For example, the built-in codec for text/*
will be selected for both text/plain
and text/html
. If there was something special that had to be done for text/html
, a more specific codec may be added for that type:ApplicationChannel.prepare
method. The codec must implement Codec
from dart:convert
. In the above example, when a response's content type is text/html
, the HTMLCodec
will encode the body object. This codec takes precedence over text/*
because it is more specific.ContentType.charset
doesn't impact which codec is selected. If a response's content-type has a charset, then a charset encoder like UTF8
will be applied as a last encoding step. For example, a response with content-type application/json; charset=utf-8
will encode the body object as a JSON string, which is then encoded as a list of UTF8 bytes. It is required that a response body's eventually encoded type is a list of bytes, so it follows that a codec that produces a string must have a charset.Response
, the body object must be a List<int>
or Stream<List<int>>
. If you find yourself converting data prior to setting it as a body object, it may make sense to add your own codec to CodecRegistry
.CodecRegistry
may specify a default charset to interpret a charset-less content-type. When a codec is added to the repository, if content-type's charset is non-null, that is the default. For example, the JSON codec is added like this:String
should not use a default charset because the repository would always attempt to decode the body as a string first.gzip
if the HTTP client allows it and the CodecRegistry
has been configured to compress the content type of the response. The three built-in codecs - application/json
, application/x-www-form-urlencoded
and text/*
- are all configured to allow compression. Compression occurs as the last step of conversion and only if the HTTP client sends the Accept-Encoding: gzip
header.Accept-Encoding
header. This is to prevent binary contents like images from being 'compressed', since they are likely already compressed by a content-specific algorithm. In order for Conduit to compress a content type other than the built-in types, you may add a codec to the repository with the allowCompression
flag. (The default value is true
.)Serializable
object can be read from a map and converted back into a map. You subclass Serializable
to assign keys from a map to properties of a your subclass, and to write its properties back to a map. This allows static types you declare in your application to represent expected request and response bodies. Conduit's ORM type ManagedObject
is a Serializable
, for example.Serializable
. Before the response is sent, asMap()
is called before the body object is encoded into JSON (or some other transmission format).Serializable
objects.Serializable
goes through three steps, whereas a List<int>
goes through zero steps and is added as-is to the HTTP response.read
and Bind.body
(when binding a Serializable
) support key filtering. A key filter is a list of keys that either discard keys from the body, requires keys in the body, or throws an error if a key exists in the body. Example:Serializable
object must implement a readFromMap()
and asMap()
.Serializable
may be used as a response body object directly:readFromMap
is invoked by read
, after all filters have been applied.