From 46677e69cebf2ff303d551c344525006816c2fb9 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Wed, 15 Mar 2017 21:11:45 -0700 Subject: [PATCH] accept strict_slash routes --- sanic/app.py | 57 +++++++++++++++++++++++++++----------------- sanic/router.py | 8 ++++++- tests/test_routes.py | 23 ++++++++++++++++++ 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/sanic/app.py b/sanic/app.py index 65a6196f..58dc38cd 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -101,7 +101,8 @@ class Sanic: return decorator # Decorator - def route(self, uri, methods=frozenset({'GET'}), host=None): + def route(self, uri, methods=frozenset({'GET'}), host=None, + strict_slashes=False): """Decorate a function to be registered as a route :param uri: path of the URL @@ -117,34 +118,42 @@ class Sanic: def response(handler): self.router.add(uri=uri, methods=methods, handler=handler, - host=host) + host=host, strict_slashes=strict_slashes) return handler return response # Shorthand method decorators - def get(self, uri, host=None): - return self.route(uri, methods=frozenset({"GET"}), host=host) + def get(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"GET"}), host=host, + strict_slashes=strict_slashes) - def post(self, uri, host=None): - return self.route(uri, methods=frozenset({"POST"}), host=host) + def post(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"POST"}), host=host, + strict_slashes=strict_slashes) - def put(self, uri, host=None): - return self.route(uri, methods=frozenset({"PUT"}), host=host) + def put(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"PUT"}), host=host, + strict_slashes=strict_slashes) - def head(self, uri, host=None): - return self.route(uri, methods=frozenset({"HEAD"}), host=host) + def head(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"HEAD"}), host=host, + strict_slashes=strict_slashes) - def options(self, uri, host=None): - return self.route(uri, methods=frozenset({"OPTIONS"}), host=host) + def options(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"OPTIONS"}), host=host, + strict_slashes=strict_slashes) - def patch(self, uri, host=None): - return self.route(uri, methods=frozenset({"PATCH"}), host=host) + def patch(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"PATCH"}), host=host, + strict_slashes=strict_slashes) - def delete(self, uri, host=None): - return self.route(uri, methods=frozenset({"DELETE"}), host=host) + def delete(self, uri, host=None, strict_slashes=False): + return self.route(uri, methods=frozenset({"DELETE"}), host=host, + strict_slashes=strict_slashes) - def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None): + def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None, + strict_slashes=False): """A helper method to register class instance or functions as a handler to the application url routes. @@ -168,11 +177,12 @@ class Sanic: if isinstance(handler, CompositionView): methods = handler.handlers.keys() - self.route(uri=uri, methods=methods, host=host)(handler) + self.route(uri=uri, methods=methods, host=host, + strict_slashes=strict_slashes)(handler) return handler # Decorator - def websocket(self, uri, host=None): + def websocket(self, uri, host=None, strict_slashes=False): """Decorate a function to be registered as a websocket route :param uri: path of the URL :param host: @@ -198,14 +208,17 @@ class Sanic: await ws.close() self.router.add(uri=uri, handler=websocket_handler, - methods=frozenset({'GET'}), host=host) + methods=frozenset({'GET'}), host=host, + strict_slashes=strict_slashes) return handler return response - def add_websocket_route(self, handler, uri, host=None): + def add_websocket_route(self, handler, uri, host=None, + strict_slashes=False): """A helper method to register a function as a websocket route.""" - return self.websocket(uri, host=host)(handler) + return self.websocket(uri, host=host, + strict_slashes=strict_slashes)(handler) def enable_websocket(self, enable=True): """Enable or disable the support for websocket. diff --git a/sanic/router.py b/sanic/router.py index 38b1c029..88924b1d 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -95,9 +95,15 @@ class Router: return name, _type, pattern - def add(self, uri, methods, handler, host=None): + def add(self, uri, methods, handler, host=None, strict_slashes=False): + # add regular version self._add(uri, methods, handler, host) + + if strict_slashes: + return + + # Add versions with and without trailing / slash_is_missing = ( not uri[-1] == '/' and not self.routes_all.get(uri + '/', False) diff --git a/tests/test_routes.py b/tests/test_routes.py index afefe4a7..3506db66 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -23,6 +23,29 @@ def test_shorthand_routes_get(): request, response = app.test_client.post('/get') assert response.status == 405 +def test_route_strict_slash(): + app = Sanic('test_route_strict_slash') + + @app.get('/get', strict_slashes=True) + def handler(request): + return text('OK') + + @app.post('/post/', strict_slashes=True) + def handler(request): + return text('OK') + + request, response = app.test_client.get('/get') + assert response.text == 'OK' + + request, response = app.test_client.get('/get/') + assert response.status == 404 + + request, response = app.test_client.post('/post/') + assert response.text == 'OK' + + request, response = app.test_client.post('/post') + assert response.status == 404 + def test_route_optional_slash(): app = Sanic('test_route_optional_slash')