Merge pull request #590 from r0fls/blueprint-strict-slash

add blueprint strict_slashes
This commit is contained in:
Raphael Deem 2017-03-23 18:42:21 -07:00 committed by GitHub
commit 179606feb1
2 changed files with 64 additions and 23 deletions

View File

@ -3,7 +3,9 @@ from collections import defaultdict, namedtuple
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.views import CompositionView from sanic.views import CompositionView
FutureRoute = namedtuple('Route', ['handler', 'uri', 'methods', 'host']) FutureRoute = namedtuple('Route',
['handler', 'uri', 'methods',
'host', 'strict_slashes'])
FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host']) FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host'])
FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs']) FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs'])
FutureException = namedtuple('Route', ['handler', 'args', 'kwargs']) FutureException = namedtuple('Route', ['handler', 'args', 'kwargs'])
@ -44,7 +46,8 @@ class Blueprint:
app.route( app.route(
uri=uri[1:] if uri.startswith('//') else uri, uri=uri[1:] if uri.startswith('//') else uri,
methods=future.methods, methods=future.methods,
host=future.host or self.host host=future.host or self.host,
strict_slashes=future.strict_slashes
)(future.handler) )(future.handler)
for future in self.websocket_routes: for future in self.websocket_routes:
@ -55,7 +58,8 @@ class Blueprint:
uri = url_prefix + future.uri if url_prefix else future.uri uri = url_prefix + future.uri if url_prefix else future.uri
app.websocket( app.websocket(
uri=uri, uri=uri,
host=future.host or self.host host=future.host or self.host,
strict_slashes=future.strict_slashes
)(future.handler) )(future.handler)
# Middleware # Middleware
@ -82,19 +86,21 @@ class Blueprint:
for listener in listeners: for listener in listeners:
app.listener(event)(listener) app.listener(event)(listener)
def route(self, uri, methods=frozenset({'GET'}), host=None): def route(self, uri, methods=frozenset({'GET'}), host=None,
strict_slashes=False):
"""Create a blueprint route from a decorated function. """Create a blueprint route from a decorated function.
:param uri: endpoint at which the route will be accessible. :param uri: endpoint at which the route will be accessible.
:param methods: list of acceptable HTTP methods. :param methods: list of acceptable HTTP methods.
""" """
def decorator(handler): def decorator(handler):
route = FutureRoute(handler, uri, methods, host) route = FutureRoute(handler, uri, methods, host, strict_slashes)
self.routes.append(route) self.routes.append(route)
return handler return handler
return decorator return decorator
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):
"""Create a blueprint route from a function. """Create a blueprint route from a function.
:param handler: function for handling uri requests. Accepts function, :param handler: function for handling uri requests. Accepts function,
@ -115,16 +121,17 @@ class Blueprint:
if isinstance(handler, CompositionView): if isinstance(handler, CompositionView):
methods = handler.handlers.keys() 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 return handler
def websocket(self, uri, host=None): def websocket(self, uri, host=None, strict_slashes=False):
"""Create a blueprint websocket route from a decorated function. """Create a blueprint websocket route from a decorated function.
:param uri: endpoint at which the route will be accessible. :param uri: endpoint at which the route will be accessible.
""" """
def decorator(handler): def decorator(handler):
route = FutureRoute(handler, uri, [], host) route = FutureRoute(handler, uri, [], host, strict_slashes)
self.websocket_routes.append(route) self.websocket_routes.append(route)
return handler return handler
return decorator return decorator
@ -183,23 +190,30 @@ class Blueprint:
self.statics.append(static) self.statics.append(static)
# Shorthand method decorators # Shorthand method decorators
def get(self, uri, host=None): def get(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["GET"], host=host) return self.route(uri, methods=["GET"], host=host,
strict_slashes=strict_slashes)
def post(self, uri, host=None): def post(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["POST"], host=host) return self.route(uri, methods=["POST"], host=host,
strict_slashes=strict_slashes)
def put(self, uri, host=None): def put(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["PUT"], host=host) return self.route(uri, methods=["PUT"], host=host,
strict_slashes=strict_slashes)
def head(self, uri, host=None): def head(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["HEAD"], host=host) return self.route(uri, methods=["HEAD"], host=host,
strict_slashes=strict_slashes)
def options(self, uri, host=None): def options(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["OPTIONS"], host=host) return self.route(uri, methods=["OPTIONS"], host=host,
strict_slashes=strict_slashes)
def patch(self, uri, host=None): def patch(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["PATCH"], host=host) return self.route(uri, methods=["PATCH"], host=host,
strict_slashes=strict_slashes)
def delete(self, uri, host=None): def delete(self, uri, host=None, strict_slashes=False):
return self.route(uri, methods=["DELETE"], host=host) return self.route(uri, methods=["DELETE"], host=host,
strict_slashes=strict_slashes)

View File

@ -24,6 +24,33 @@ def test_bp():
assert response.text == 'Hello' assert response.text == 'Hello'
def test_bp_strict_slash():
app = Sanic('test_route_strict_slash')
bp = Blueprint('test_text')
@bp.get('/get', strict_slashes=True)
def handler(request):
return text('OK')
@bp.post('/post/', strict_slashes=True)
def handler(request):
return text('OK')
app.blueprint(bp)
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_bp_with_url_prefix(): def test_bp_with_url_prefix():
app = Sanic('test_text') app = Sanic('test_text')
bp = Blueprint('test_text', url_prefix='/test1') bp = Blueprint('test_text', url_prefix='/test1')