Make default handler customizable
diff --git a/pkgs/shelf_router/CHANGELOG.md b/pkgs/shelf_router/CHANGELOG.md
index 49ab39f..ee8b207 100644
--- a/pkgs/shelf_router/CHANGELOG.md
+++ b/pkgs/shelf_router/CHANGELOG.md
@@ -1,6 +1,10 @@
 ## v0.8.0-nullsafety.0
 
  * Migrate package to null-safety
+ * Since handlers are not allowed to return `null` in `shelf` 1.0.0, a router
+   will return a default 404 response instead.
+   This behavior can be overridden with the `notFoundHandler` constructor
+   parameter.
 
 ## v0.7.4
 
diff --git a/pkgs/shelf_router/lib/src/router.dart b/pkgs/shelf_router/lib/src/router.dart
index c0012d2..51f96fd 100644
--- a/pkgs/shelf_router/lib/src/router.dart
+++ b/pkgs/shelf_router/lib/src/router.dart
@@ -26,7 +26,7 @@
   if (!(p is Map<String, String>)) {
     throw Exception('no such parameter $name');
   }
-  final value = (p)[name];
+  final value = p[name];
   if (value == null) {
     throw Exception('no such parameter $name');
   }
@@ -78,13 +78,21 @@
 /// ```
 ///
 /// If multiple routes match the same request, the handler for the first
-/// route is called. If the handler returns `null` the next matching handler
-/// will be attempted.
-///
-///
+/// route is called.
+/// If no route matches a request, a [Response.notFound] will be returned
+/// instead. The default matcher can be overridden with the `notFoundHandler`
+/// constructor parameter.
 @sealed
 class Router {
   final List<RouterEntry> _routes = [];
+  final Handler _notFoundHandler;
+
+  /// Creates a new [Router] routing requests to handlers.
+  ///
+  /// The [notFoundHandler] will be invoked for requests where no matching route
+  /// was found. By default, a simple [Response.notFound] will be used instead.
+  Router({Handler notFoundHandler = _defaultNotFound})
+      : _notFoundHandler = notFoundHandler;
 
   /// Add [handler] for [verb] requests to [route].
   ///
@@ -149,7 +157,7 @@
         return await route.invoke(request, params);
       }
     }
-    return Response.notFound('Not Found');
+    return _notFoundHandler(request);
   }
 
   // Handlers for all methods
@@ -185,4 +193,8 @@
 
   /// Handle `PATCH` request to [route] using [handler].
   void patch(String route, Function handler) => add('PATCH', route, handler);
+
+  static Response _defaultNotFound(Request request) {
+    return Response.notFound('Not Found');
+  }
 }
diff --git a/pkgs/shelf_router/test/router_test.dart b/pkgs/shelf_router/test/router_test.dart
index e5997dd..01ca321 100644
--- a/pkgs/shelf_router/test/router_test.dart
+++ b/pkgs/shelf_router/test/router_test.dart
@@ -147,4 +147,22 @@
     expect(await get('/api/hello'), 'Hello');
     expect(await get('/api/hello?ok'), 'middleware');
   });
+
+  test('responds with 404 if no handler matches', () {
+    var api = Router()..get('/hello', (request) => Response.ok('Hello'));
+    server.mount(api);
+
+    expect(
+        get('/hi'),
+        throwsA(isA<http.ClientException>()
+            .having((e) => e.message, 'message', contains('404: Not Found.'))));
+  });
+
+  test('can invoke custom handler if no route matches', () {
+    var api = Router(notFoundHandler: (req) => Response.ok('Not found, but ok'))
+      ..get('/hello', (request) => Response.ok('Hello'));
+    server.mount(api);
+
+    expect(get('/hi'), completion('Not found, but ok'));
+  });
 }