mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:58:32 +00:00
Add a context field to Message in shelf.
Originally https://codereview.chromium.org/226263007/. BUG=https://code.google.com/p/dart/issues/detail?id=17992 R=kevmoo@google.com Review URL: https://codereview.chromium.org//239183005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@35102 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
da7e681b60
commit
f11614bb45
3
AUTHORS
3
AUTHORS
|
@ -28,4 +28,5 @@ Nikolaus Graf <nik@blossom.io>
|
|||
Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
|
||||
Victor Berchet <victor.berchet@gmail.com>
|
||||
Roel Spilker <r.spilker@gmail.com>
|
||||
Martin Charles <martincharles07@gmail.com>
|
||||
Martin Charles <martincharles07@gmail.com>
|
||||
Anders Holmgren <andersmholmgren@gmail.com>
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
## 0.5.1
|
||||
|
||||
* Add a `context` map to `Request` and `Response` for passing data among
|
||||
handlers and middleware.
|
||||
|
||||
## 0.5.0+1
|
||||
|
||||
* Allow `scheduled_test` development dependency up to v0.12.0
|
||||
|
|
|
@ -20,6 +20,19 @@ abstract class Message {
|
|||
/// The value is immutable.
|
||||
final Map<String, String> headers;
|
||||
|
||||
/// Extra context that can be used by for middleware and handlers.
|
||||
///
|
||||
/// For requests, this is used to pass data to inner middleware and handlers;
|
||||
/// for responses, it's used to pass data to outer middleware and handlers.
|
||||
///
|
||||
/// Context properties that are used by a particular package should begin with
|
||||
/// that package's name followed by a period. For example, if [logRequests]
|
||||
/// wanted to take a prefix, its property name would be `"shelf.prefix"`,
|
||||
/// since it's in the `shelf` package.
|
||||
///
|
||||
/// The value is immutable.
|
||||
final Map<String, Object> context;
|
||||
|
||||
/// The streaming body of the message.
|
||||
///
|
||||
/// This can be read via [read] or [readAsString].
|
||||
|
@ -28,8 +41,11 @@ abstract class Message {
|
|||
/// Creates a new [Message].
|
||||
///
|
||||
/// If [headers] is `null`, it is treated as empty.
|
||||
Message(this._body, {Map<String, String> headers})
|
||||
: this.headers = _getIgnoreCaseMapView(headers);
|
||||
Message(this._body, {Map<String, String> headers,
|
||||
Map<String, Object> context})
|
||||
: this.headers = _getIgnoreCaseMapView(headers),
|
||||
this.context = new pc.UnmodifiableMapView(
|
||||
context == null ? const {} : new Map.from(context));
|
||||
|
||||
/// The contents of the content-length field in [headers].
|
||||
///
|
||||
|
|
|
@ -71,14 +71,14 @@ class Request extends Message {
|
|||
// TODO(kevmoo) finish documenting the rest of the arguments.
|
||||
Request(this.method, Uri requestedUri, {String protocolVersion,
|
||||
Map<String, String> headers, Uri url, String scriptName,
|
||||
Stream<List<int>> body})
|
||||
Stream<List<int>> body, Map<String, Object> context})
|
||||
: this.requestedUri = requestedUri,
|
||||
this.protocolVersion = protocolVersion == null ?
|
||||
'1.1' : protocolVersion,
|
||||
this.url = _computeUrl(requestedUri, url, scriptName),
|
||||
this.scriptName = _computeScriptName(requestedUri, url, scriptName),
|
||||
super(body == null ? new Stream.fromIterable([]) : body,
|
||||
headers: headers) {
|
||||
headers: headers, context: context) {
|
||||
if (method.isEmpty) throw new ArgumentError('method cannot be empty.');
|
||||
|
||||
// TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed
|
||||
|
|
|
@ -53,8 +53,10 @@ class Response extends Message {
|
|||
/// If [encoding] is passed, the "encoding" field of the Content-Type header
|
||||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.ok(body, {Map<String, String> headers, Encoding encoding})
|
||||
: this(200, body: body, headers: headers, encoding: encoding);
|
||||
Response.ok(body, {Map<String, String> headers, Encoding encoding,
|
||||
Map<String, Object> context})
|
||||
: this(200, body: body, headers: headers, encoding: encoding,
|
||||
context: context);
|
||||
|
||||
/// Constructs a 301 Moved Permanently response.
|
||||
///
|
||||
|
@ -71,8 +73,9 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.movedPermanently(location, {body, Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
: this._redirect(301, location, body, headers, encoding);
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: this._redirect(301, location, body, headers, encoding,
|
||||
context: context);
|
||||
|
||||
/// Constructs a 302 Found response.
|
||||
///
|
||||
|
@ -89,8 +92,9 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.found(location, {body, Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
: this._redirect(302, location, body, headers, encoding);
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: this._redirect(302, location, body, headers, encoding,
|
||||
context: context);
|
||||
|
||||
/// Constructs a 303 See Other response.
|
||||
///
|
||||
|
@ -108,17 +112,20 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.seeOther(location, {body, Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
: this._redirect(303, location, body, headers, encoding);
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: this._redirect(303, location, body, headers, encoding,
|
||||
context: context);
|
||||
|
||||
/// Constructs a helper constructor for redirect responses.
|
||||
Response._redirect(int statusCode, location, body,
|
||||
Map<String, String> headers, Encoding encoding)
|
||||
Map<String, String> headers, Encoding encoding,
|
||||
{ Map<String, Object> context })
|
||||
: this(statusCode,
|
||||
body: body,
|
||||
encoding: encoding,
|
||||
headers: _addHeader(
|
||||
headers, 'location', _locationToString(location)));
|
||||
headers, 'location', _locationToString(location)),
|
||||
context: context);
|
||||
|
||||
/// Constructs a 304 Not Modified response.
|
||||
///
|
||||
|
@ -126,9 +133,11 @@ class Response extends Message {
|
|||
/// information used to determine whether the requested resource has changed
|
||||
/// since the last request. It indicates that the resource has not changed and
|
||||
/// the old value should be used.
|
||||
Response.notModified({Map<String, String> headers})
|
||||
Response.notModified({Map<String, String> headers,
|
||||
Map<String, Object> context})
|
||||
: this(304, headers: _addHeader(
|
||||
headers, 'date', formatHttpDate(new DateTime.now())));
|
||||
headers, 'date', formatHttpDate(new DateTime.now())),
|
||||
context: context);
|
||||
|
||||
/// Constructs a 403 Forbidden response.
|
||||
///
|
||||
|
@ -143,8 +152,9 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.forbidden(body, {Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
: this(403, body: body, headers: headers);
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: this(403, body: body, headers: headers,
|
||||
context: context);
|
||||
|
||||
/// Constructs a 404 Not Found response.
|
||||
///
|
||||
|
@ -159,8 +169,10 @@ class Response extends Message {
|
|||
/// If [encoding] is passed, the "encoding" field of the Content-Type header
|
||||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.notFound(body, {Map<String, String> headers, Encoding encoding})
|
||||
: this(404, body: body, headers: headers);
|
||||
Response.notFound(body, {Map<String, String> headers, Encoding encoding,
|
||||
Map<String, Object> context})
|
||||
: this(404, body: body, headers: headers,
|
||||
context: context);
|
||||
|
||||
/// Constructs a 500 Internal Server Error response.
|
||||
///
|
||||
|
@ -176,10 +188,11 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response.internalServerError({body, Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: this(500,
|
||||
headers: body == null ? _adjust500Headers(headers) : headers,
|
||||
body: body == null ? 'Internal Server Error' : body);
|
||||
body: body == null ? 'Internal Server Error' : body,
|
||||
context: context);
|
||||
|
||||
/// Constructs an HTTP response with the given [statusCode].
|
||||
///
|
||||
|
@ -194,9 +207,10 @@ class Response extends Message {
|
|||
/// in [headers] will be set appropriately. If there is no existing
|
||||
/// Content-Type header, it will be set to "application/octet-stream".
|
||||
Response(this.statusCode, {body, Map<String, String> headers,
|
||||
Encoding encoding})
|
||||
Encoding encoding, Map<String, Object> context})
|
||||
: super(_bodyToStream(body, encoding),
|
||||
headers: _adjustHeaders(headers, encoding)) {
|
||||
headers: _adjustHeaders(headers, encoding),
|
||||
context: context) {
|
||||
if (statusCode < 100) {
|
||||
throw new ArgumentError("Invalid status code: $statusCode.");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: shelf
|
||||
version: 0.5.0+1
|
||||
version: 0.5.1-dev
|
||||
author: Dart Team <misc@dartlang.org>
|
||||
description: Web Server Middleware for Dart
|
||||
homepage: http://www.dartlang.org
|
||||
|
|
|
@ -11,13 +11,15 @@ import 'package:shelf/src/message.dart';
|
|||
import 'package:unittest/unittest.dart';
|
||||
|
||||
class _TestMessage extends Message {
|
||||
_TestMessage(Map<String, String> headers, Stream<List<int>> body)
|
||||
: super(body, headers: headers);
|
||||
_TestMessage(Map<String, String> headers, Map<String, Object> context,
|
||||
Stream<List<int>> body)
|
||||
: super(body, headers: headers, context: context);
|
||||
}
|
||||
|
||||
Message _createMessage({Map<String, String> headers, Stream<List<int>> body}) {
|
||||
Message _createMessage({Map<String, String> headers,
|
||||
Map<String, Object> context, Stream<List<int>> body}) {
|
||||
if (body == null) body = new Stream.fromIterable([]);
|
||||
return new _TestMessage(headers, body);
|
||||
return new _TestMessage(headers, context, body);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
@ -43,6 +45,26 @@ void main() {
|
|||
expect(() => message.headers['h2'] = 'value2', throwsUnsupportedError);
|
||||
});
|
||||
});
|
||||
|
||||
group('context', () {
|
||||
test('is accessible', () {
|
||||
var message = _createMessage(context: {'foo': 'bar'});
|
||||
expect(message.context, containsPair('foo', 'bar'));
|
||||
});
|
||||
|
||||
test('null context value becomes empty and immutable', () {
|
||||
var message = _createMessage();
|
||||
expect(message.context, isEmpty);
|
||||
expect(() => message.context['key'] = 'value', throwsUnsupportedError);
|
||||
});
|
||||
|
||||
test('is immutable', () {
|
||||
var message = _createMessage(context: {'key': 'value'});
|
||||
expect(() => message.context['key'] = 'value', throwsUnsupportedError);
|
||||
expect(() => message.context['key2'] = 'value', throwsUnsupportedError);
|
||||
});
|
||||
});
|
||||
|
||||
group("readAsString", () {
|
||||
test("supports a null body", () {
|
||||
var request = _createMessage();
|
||||
|
|
Loading…
Reference in a new issue