Skip to content

Commit 76b2a2a

Browse files
authored
feat(dart_frog): expand router http method support (#76)
1 parent 19b96cb commit 76b2a2a

File tree

5 files changed

+310
-32
lines changed

5 files changed

+310
-32
lines changed

packages/dart_frog/lib/src/_internal.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:convert';
22
import 'dart:io';
33

44
import 'package:dart_frog/dart_frog.dart';
5+
import 'package:http_methods/http_methods.dart' show isHttpMethod;
56
import 'package:shelf/shelf.dart' as shelf;
67
import 'package:shelf/shelf_io.dart' as shelf_io;
78

packages/dart_frog/lib/src/http_method.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ enum HttpMethod {
88
/// GET HTTP Method
99
get('GET'),
1010

11+
/// HEAD HTTP Method
12+
head('HEAD'),
13+
14+
/// OPTIONS HTTP Method
15+
options('OPTIONS'),
16+
1117
/// PATCH HTTP Method
1218
patch('PATCH'),
1319

packages/dart_frog/lib/src/router.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818

1919
part of '_internal.dart';
2020

21+
/// Middleware to remove body from request.
22+
Handler _removeBody(Handler handler) {
23+
return (context) async {
24+
var response = await handler(context);
25+
if (response.headers.containsKey('content-length')) {
26+
response = response.copyWith(headers: {'content-length': '0'});
27+
}
28+
return response.copyWith(body: <int>[]);
29+
};
30+
}
31+
2132
/// A class that routes requests to handlers based on HTTP verb and route
2233
/// pattern.
2334
class Router {
@@ -31,6 +42,27 @@ class Router {
3142
final List<RouterEntry> _routes = [];
3243
final Handler _notFoundHandler;
3344

45+
/// Add [handler] for [verb] requests to [route].
46+
///
47+
/// If [verb] is `GET` the [handler] will also be called for `HEAD` requests
48+
/// matching [route]. This is because handling `GET` requests without handling
49+
/// `HEAD` is always wrong. To explicitely implement a `HEAD` handler it must
50+
/// be registered before the `GET` handler.
51+
void add(String verb, String route, Function handler) {
52+
if (!isHttpMethod(verb)) {
53+
throw ArgumentError.value(verb, 'verb', 'expected a valid HTTP method');
54+
}
55+
56+
final _verb = verb.toUpperCase();
57+
58+
if (_verb == 'GET') {
59+
// Handling in a 'GET' request without handling a 'HEAD' request is always
60+
// wrong, thus, we add a default implementation that discards the body.
61+
_routes.add(RouterEntry('HEAD', route, handler, middleware: _removeBody));
62+
}
63+
_routes.add(RouterEntry(_verb, route, handler));
64+
}
65+
3466
/// Handle all request to [route] using [handler].
3567
void all(String route, Function handler) {
3668
_routes.add(RouterEntry('ALL', route, handler));
@@ -86,6 +118,33 @@ class Router {
86118
return _notFoundHandler(context);
87119
}
88120

121+
// Handlers for all methods
122+
123+
/// Handle `GET` request to [route] using [handler].
124+
///
125+
/// If no matching handler for `HEAD` requests is registered, such requests
126+
/// will also be routed to the [handler] registered here.
127+
void get(String route, Function handler) => add('GET', route, handler);
128+
129+
/// Handle `HEAD` request to [route] using [handler].
130+
void head(String route, Function handler) => add('HEAD', route, handler);
131+
132+
/// Handle `POST` request to [route] using [handler].
133+
void post(String route, Function handler) => add('POST', route, handler);
134+
135+
/// Handle `PUT` request to [route] using [handler].
136+
void put(String route, Function handler) => add('PUT', route, handler);
137+
138+
/// Handle `DELETE` request to [route] using [handler].
139+
void delete(String route, Function handler) => add('DELETE', route, handler);
140+
141+
/// Handle `OPTIONS` request to [route] using [handler].
142+
void options(String route, Function handler) =>
143+
add('OPTIONS', route, handler);
144+
145+
/// Handle `PATCH` request to [route] using [handler].
146+
void patch(String route, Function handler) => add('PATCH', route, handler);
147+
89148
static Response _defaultNotFound(RequestContext context) => routeNotFound;
90149

91150
/// Sentinel [Response] object indicating that no matching route was found.

packages/dart_frog/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ environment:
77
sdk: ">=2.17.0 <3.0.0"
88

99
dependencies:
10+
http_methods: ^1.1.0
1011
shelf: ^1.1.0
1112
shelf_hotreload: ^1.1.0
1213

0 commit comments

Comments
 (0)