Strategy interface for handling the return value of a JetLeaf controller method invocation.
A ReturnValueHandler interprets the value returned by a controller method and performs a corresponding action — such as:
- Rendering a view
- Writing text or binary data to the HTTP response
- Serializing an object to JSON or another format
- Setting response headers and status codes
- Handling redirects and forward operations
This abstraction enables flexible return value handling and allows the framework to support multiple response paradigms (MVC, REST, etc.) without coupling controller methods to low-level HTTP operations.
Resolution Process
When a controller method completes execution, the framework:
- Iterates through registered handlers in order
- Calls canHandle to find a compatible handler
- Invokes handleReturnValue on the first matching handler
- Stops processing after the first successful resolution
Responsibilities
- Determine if the handler can handle a given return type or value via canHandle
- Process and render the value appropriately via handleReturnValue
- Set appropriate HTTP response headers and status codes
- Handle any rendering errors gracefully
Built-in Handler Implementations
JetLeaf provides several standard handlers:
ViewNameReturnValueHandler→ Handles string return values as view namesPageViewReturnValueHandler→ Processes PageView instancesResponseBodyReturnValueHandler→ Handles ResponseBody wrapper objectsJsonReturnValueHandler→ Serializes objects to JSON with proper content typeStringReturnValueHandler→ Writes plain text responsesVoidReturnValueHandler→ Handles methods that returnvoidornullRedirectReturnValueHandler→ Processes redirect instructions
Example: Custom JSON Handler
class JsonReturnValueHandler implements ReturnValueHandler {
final JsonEncoder _encoder = JsonEncoder();
@override
bool canHandle(Method method, Object? returnValue, ServerHttpRequest req) {
// Handle any non-primitive object that isn't a framework type
return returnValue != null &&
!_isFrameworkType(returnValue) &&
!_isPrimitive(returnValue);
}
@override
Future<void> handleReturnValue(
Object? returnValue,
Method method,
ServerHttpRequest req,
ServerHttpResponse res,
HandlerMethod handler,
) async {
// Set JSON content type
res.headers.setContentType(MediaType.APPLICATION_JSON);
// Serialize and write response
final jsonString = _encoder.encode(returnValue);
await res.body.writeString(jsonString, Closeable.DEFAULT_ENCODING);
}
bool _isFrameworkType(Object value) {
return value is PageView ||
value is ResponseBody ||
value is View;
}
bool _isPrimitive(Object value) {
return value is String ||
value is num ||
value is bool ||
value is List ||
value is Map;
}
}
Example: File Download Handler
class FileDownloadReturnValueHandler implements ReturnValueHandler {
@override
bool canHandle(Method method, Object? returnValue, ServerHttpRequest req) {
return returnValue is FileDownload;
}
@override
Future<void> handleReturnValue(
Object? returnValue,
Method method,
ServerHttpRequest req,
ServerHttpResponse res,
HandlerMethod handler,
) async {
final download = returnValue as FileDownload;
// Set download headers
res.headers
..setContentType(download.contentType)
..set('Content-Disposition', 'attachment; filename="${download.filename}"')
..setContentLength(download.contentLength);
// Stream file content
await download.content.pipe(res.body);
}
}
Framework Integration
- Handlers are registered in the
HandlerMethodAdapterconfiguration - Execution order is determined by registration order
- Custom handlers can be added to extend framework capabilities
- Handlers are typically stateless and thread-safe
Design Notes
- This interface is part of the internal controller resolution system
- Each handler must be stateless and reusable across requests
- Resolution order is managed by JetLeaf's
HandlerMethodAdapter - Implementations should avoid blocking operations during rendering
- Handlers should handle errors gracefully and provide meaningful feedback
Best Practices
- Make canHandle checks as efficient as possible
- Handle null return values appropriately
- Set proper HTTP headers and status codes
- Use async operations for I/O-intensive tasks
- Provide clear error messages for debugging
- Consider content negotiation when applicable
Related Components
- HandlerMethod - The controller method being invoked
- ServerHttpRequest - The incoming HTTP request
- ServerHttpResponse - The outgoing HTTP response
HandlerMethodAdapter- Coordinates the resolution process
- Implementers
- Annotations
-
- @Author.new("Evaristus Adimonyemma")
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
Methods
-
canHandle(
Method? method, Object? returnValue, ServerHttpRequest request) → bool - Determines whether this handler can handle the given return value.
-
equalizedProperties(
) → List< Object?> -
Mixin-style contract for value-based equality,
hashCode, andtoString.inherited -
getSupportedMediaTypes(
) → List< MediaType> - Returns the list of media types this handler supports by default.
-
handleReturnValue(
Object? returnValue, Method? method, ServerHttpRequest request, ServerHttpResponse response, HandlerMethod? hm) → Future< void> - Processes the return value and writes the appropriate response.
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited