library angel_framework.http.response_context; import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:angel_route/angel_route.dart'; import 'package:json_god/json_god.dart' as god; import 'package:mime/mime.dart'; import '../extensible.dart'; import 'angel_base.dart'; import 'controller.dart'; /// A convenience wrapper around an outgoing HTTP request. class ResponseContext extends Extensible { bool _isOpen = true; /// The [Angel] instance that is sending a response. AngelBase app; /// Can we still write to this response? bool get isOpen => _isOpen; /// A set of UTF-8 encoded bytes that will be written to the response. final BytesBuilder buffer = new BytesBuilder(); /// Sets the status code to be sent with this response. void status(int code) { underlyingResponse.statusCode = code; } /// The underlying [HttpResponse] under this instance. final HttpResponse underlyingResponse; ResponseContext(this.underlyingResponse, this.app); /// Any and all cookies to be sent to the user. List get cookies => underlyingResponse.cookies; /// Set this to true if you will manually close the response. bool willCloseItself = false; /// Sends a download as a response. download(File file, {String filename}) async { header("Content-Disposition", 'attachment; filename="${filename ?? file.path}"'); header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path)); header(HttpHeaders.CONTENT_LENGTH, file.lengthSync().toString()); buffer.add(await file.readAsBytes()); end(); } /// Prevents more data from being written to the response. void end() { _isOpen = false; } /// Sets a response header to the given value, or retrieves its value. header(String key, [String value]) { if (value == null) return underlyingResponse.headers[key]; else underlyingResponse.headers.set(key, value); } /// Serializes JSON to the response. void json(value) { write(god.serialize(value)); header(HttpHeaders.CONTENT_TYPE, ContentType.JSON.toString()); end(); } /// Returns a JSONP response. void jsonp(value, {String callbackName: "callback"}) { write("$callbackName(${god.serialize(value)})"); header(HttpHeaders.CONTENT_TYPE, "application/javascript"); end(); } /// Renders a view to the response stream, and closes the response. Future render(String view, [Map data]) async { write(await app.viewGenerator(view, data)); header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString()); end(); } /// Redirects to user to the given URL. void redirect(String url, {int code: 301}) { header(HttpHeaders.LOCATION, url); status(code ?? 301); write(''' Redirecting...

Currently redirecting you...


Click here if you are not automatically redirected... '''); end(); } /// Redirects to the given named [Route]. void redirectTo(String name, [Map params, int code]) { // Todo: Need to recurse route hierarchy, but also efficiently :) Route matched = app.routes.firstWhere((Route route) => route.name == name); if (matched != null) { redirect(matched.makeUri(params), code: code); return; } throw new ArgumentError.notNull('Route to redirect to ($name)'); } /// Redirects to the given [Controller] action. void redirectToAction(String action, [Map params, int code]) { // UserController@show List split = action.split("@"); // Todo: AngelResponseException if (split.length < 2) throw new Exception( "Controller redirects must take the form of 'Controller@action'. You gave: $action"); Controller controller = app.controller(split[0]); if (controller == null) throw new Exception("Could not find a controller named '${split[0]}'"); Route matched = controller.routeMappings[split[1]]; if (matched == null) throw new Exception( "Controller '${split[0]}' does not contain any action named '${split[1]}'"); redirect(matched.makeUri(params), code: code); } /// Streams a file to this response as chunked data. /// /// Useful for video sites. Future streamFile(File file, {int chunkSize, int sleepMs: 0, bool resumable: true}) async { if (!isOpen) return; header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path)); willCloseItself = true; await file.openRead().pipe(underlyingResponse); } /// Writes data to the response. void write(value, {Encoding encoding: UTF8}) { if (isOpen) { if (value is List) buffer.add(value); else buffer.add(encoding.encode(value.toString())); } } }