|
| 1 | +library shelf_routing; |
| 2 | + |
| 3 | +import 'package:meta/meta_meta.dart'; |
| 4 | + |
| 5 | +export 'package:shelf_routing/src/get_request_extension.dart'; |
| 6 | +export 'package:shelf_routing/src/json_response.dart'; |
| 7 | +export 'package:shelf_routing/src/utils.dart'; |
| 8 | + |
| 9 | +/// Annotation for top level getters to generate a router that handles all routers of the types |
| 10 | +/// passed using the package `shelf_routing_generator`. |
| 11 | +/// |
| 12 | +/// **Example** |
| 13 | +/// ```dart |
| 14 | +/// // Always import 'shelf_router' without 'show' or 'as'. |
| 15 | +/// import 'package:shelf_router/shelf_router.dart'; |
| 16 | +/// // Always import 'shelf_routing' without 'show' or 'as'. |
| 17 | +/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response; |
| 18 | +/// |
| 19 | +/// // Include generated code, this assumes current file is 'services_router.dart'. |
| 20 | +/// part 'services_router.g.dart'; |
| 21 | +/// |
| 22 | +/// // Get a router for all these services. |
| 23 | +/// @GenerateRouterFor([MyService, MyAnotherService]) |
| 24 | +/// Router get router => _$router; |
| 25 | +/// ``` |
| 26 | +@Target({TargetKind.getter}) |
| 27 | +class GenerateRouterFor { |
| 28 | + /// List of services that have a static getter that returns a Router |
| 29 | + final List<Type> routables; |
| 30 | + |
| 31 | + /// See class documentation |
| 32 | + const GenerateRouterFor(this.routables); |
| 33 | +} |
| 34 | + |
| 35 | +/// Annotation for services using on methods the Route annotation to add a [prefix] when the router |
| 36 | +/// is mounted via the [GenerateRouterFor] annotation using the package `shelf_routing_generator`. |
| 37 | +/// |
| 38 | +/// **Example** |
| 39 | +/// ```dart |
| 40 | +/// // Always import 'shelf_router' without 'show' or 'as'. |
| 41 | +/// import 'package:shelf_router/shelf_router.dart'; |
| 42 | +/// // Always import 'shelf_routing' without 'show' or 'as'. |
| 43 | +/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response; |
| 44 | +/// |
| 45 | +/// // Include generated code, this assumes current file is 'service.dart'. |
| 46 | +/// part 'service.g.dart'; |
| 47 | +/// |
| 48 | +/// // Get a router for all these services. |
| 49 | +/// @Routable(prefix: '/say-hello') |
| 50 | +/// class Service { |
| 51 | +/// @Route.get('/<name>') |
| 52 | +/// Future<Response> _sayHello(Request request, String name) async { |
| 53 | +/// return Response.ok('hello $name'); |
| 54 | +/// } |
| 55 | +/// |
| 56 | +/// /// Get a router for this service. |
| 57 | +/// Router get router => _$ServiceRouter(this); |
| 58 | +/// } |
| 59 | +/// ``` |
| 60 | +@Target({TargetKind.classType}) |
| 61 | +class Routable { |
| 62 | + /// Router prefix |
| 63 | + final String prefix; |
| 64 | + |
| 65 | + /// See class documentation |
| 66 | + const Routable({required this.prefix}); |
| 67 | +} |
| 68 | + |
| 69 | +/// Annotation for methods using Route annotation to indicate that the route should be called with |
| 70 | +/// this the header [name] using the package `shelf_routing_generator`. |
| 71 | +/// |
| 72 | +/// **Example** |
| 73 | +/// ```dart |
| 74 | +/// import 'package:shelf/shelf.dart'; |
| 75 | +/// // Always import 'shelf_routing' without 'show' or 'as'. |
| 76 | +/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response; |
| 77 | +/// |
| 78 | +/// // Include generated code, this assumes current file is 'service.dart'. |
| 79 | +/// part 'service.g.dart'; |
| 80 | +/// |
| 81 | +/// class Service { |
| 82 | +/// @Route.get('/<name>') |
| 83 | +/// @RouteHeader(name: 'authentication') |
| 84 | +/// Future<Response> _sayHello(Request request, String name) async { |
| 85 | +/// return Response.ok('hello $name'); |
| 86 | +/// } |
| 87 | +/// } |
| 88 | +/// ``` |
| 89 | +@Target({TargetKind.method}) |
| 90 | +class RouteHeader { |
| 91 | + /// The header name |
| 92 | + final String name; |
| 93 | + |
| 94 | + /// See class documentation |
| 95 | + const RouteHeader({required this.name}); |
| 96 | +} |
| 97 | + |
| 98 | +/// Identifies the data type of the request. |
| 99 | +enum BadRequestPosition { |
| 100 | + /// Request header |
| 101 | + header, |
| 102 | + |
| 103 | + /// Path parameter |
| 104 | + path, |
| 105 | + |
| 106 | + /// Query parameter |
| 107 | + queryParameter, |
| 108 | + |
| 109 | + /// Request body |
| 110 | + body |
| 111 | +} |
| 112 | + |
| 113 | +/// The exception that will be thrown in case the validation/parsing of the request fails. |
| 114 | +class BadRequestException implements Exception { |
| 115 | + /// Identifies the type of invalid data. |
| 116 | + final BadRequestPosition position; |
| 117 | + |
| 118 | + /// The error generated by validation/parsing. |
| 119 | + final Object error; |
| 120 | + |
| 121 | + /// The stack trace generated by validation/parsing. |
| 122 | + final StackTrace stackTrace; |
| 123 | + |
| 124 | + /// The invalid field name. |
| 125 | + final String? name; |
| 126 | + |
| 127 | + /// Constructor for a request header error. |
| 128 | + BadRequestException.header( |
| 129 | + this.error, |
| 130 | + this.stackTrace, |
| 131 | + String this.name, |
| 132 | + ) : position = BadRequestPosition.header; |
| 133 | + |
| 134 | + /// Constructor for a request path parameter error. |
| 135 | + BadRequestException.path(this.error, this.stackTrace) |
| 136 | + : position = BadRequestPosition.path, |
| 137 | + name = null; |
| 138 | + |
| 139 | + /// Constructor for a request query parameter error. |
| 140 | + BadRequestException.queryParameter(this.error, this.stackTrace, String this.name) |
| 141 | + : position = BadRequestPosition.queryParameter; |
| 142 | + |
| 143 | + /// Constructor for a request body error. |
| 144 | + BadRequestException.body(this.error, this.stackTrace) |
| 145 | + : position = BadRequestPosition.body, |
| 146 | + name = null; |
| 147 | + |
| 148 | + @override |
| 149 | + String toString() => 'Missing or invalid ${position.name}${name != null ? ' "$name"' : ''}.'; |
| 150 | +} |
0 commit comments