diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d8763e2..dae87e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Allow setting of `bodyAsObject`, `bodyAsMap`, or `bodyAsList` **exactly once**. * Resolve named singletons in `resolveInjection`. * Fix a bug where `Service.parseId` would attempt to parse an `int`. +* Replace as Data cast in Service.dart with a method that throws a 400 on error. # 2.0.0-alpha.24 * Add `AngelEnv` class to `core`. diff --git a/lib/src/core/service.dart b/lib/src/core/service.dart index 52104df0..0fcfc5a7 100644 --- a/lib/src/core/service.dart +++ b/lib/src/core/service.dart @@ -8,6 +8,7 @@ import '../util.dart'; import 'anonymous_service.dart'; import 'hooked_service.dart' show HookedService; import 'metadata.dart'; +import 'request_context.dart'; import 'response_context.dart'; import 'routable.dart'; import 'server.dart'; @@ -72,6 +73,26 @@ class Service extends Routable { /// Closes this service, including any database connections or stream controllers. void close() {} + /// An optional [readData] function can be passed to handle non-map/non-json bodies. + Service({FutureOr Function(RequestContext, ResponseContext) readData}) { + _readData = readData ?? + (req, res) { + if (req.bodyAsObject is! Data) { + throw AngelHttpException.badRequest( + message: + 'Invalid request body. Expected $Data; found ${req.bodyAsObject} instead.'); + } else { + return req.bodyAsObject as Data; + } + }; + } + + FutureOr Function(RequestContext, ResponseContext) _readData; + + /// A [Function] that reads the request body and converts it into [Data]. + FutureOr Function(RequestContext, ResponseContext) get readData => + _readData; + /// Retrieves the first object from the result of calling [index] with the given [params]. /// /// If the result of [index] is `null`, OR an empty [Iterable], a 404 `AngelHttpException` will be thrown. @@ -215,10 +236,10 @@ class Service extends Routable { Middleware createMiddleware = getAnnotation(service.create, app.container.reflector); post('/', (req, ResponseContext res) { - return req.parseBody().then((_) { - return this + return req.parseBody().then((_) async { + return await this .create( - req.bodyAsMap as Data, + await readData(req, res), mergeMap([ {'query': req.queryParameters}, restProvider, @@ -254,10 +275,10 @@ class Service extends Routable { Middleware modifyMiddleware = getAnnotation(service.modify, app.container.reflector); patch('/:id', (req, res) { - return req.parseBody().then((_) { - return this.modify( + return req.parseBody().then((_) async { + return await this.modify( parseId(req.params['id']), - req.bodyAsMap as Data, + await readData(req, res), mergeMap([ {'query': req.queryParameters}, restProvider, @@ -273,10 +294,10 @@ class Service extends Routable { Middleware updateMiddleware = getAnnotation(service.update, app.container.reflector); post('/:id', (req, res) { - return req.parseBody().then((_) { - return this.update( + return req.parseBody().then((_) async { + return await this.update( parseId(req.params['id']), - req.bodyAsMap as Data, + await readData(req, res), mergeMap([ {'query': req.queryParameters}, restProvider, @@ -289,10 +310,10 @@ class Service extends Routable { ..addAll( (updateMiddleware == null) ? [] : updateMiddleware.handlers)); put('/:id', (req, res) { - return req.parseBody().then((_) { - return this.update( + return req.parseBody().then((_) async { + return await this.update( parseId(req.params['id']), - req.bodyAsMap as Data, + await readData(req, res), mergeMap([ {'query': req.queryParameters}, restProvider,