Repository: sanic-org/sanic

Issue:
Decorators not applied to Static Files
**Describe the bug**

decorators cannot be applied to static files serving. My use-case here is serving auto-generated docs through the application itself while requesting a valid Basic-Authentication for the static files.


**Code snippet**
```python
import os

from sanic import Blueprint, Sanic
from sanic.request import Request
from sanic_httpauth import HTTPBasicAuth

auth = HTTPBasicAuth()

users = {
    "docs": "docs",
}


@auth.verify_password
def verify_password(username: str, password: str) -> bool:
    """
    basic auth credentials validation

    :param username:
    :param password:
    :return:
    """
    if username in users:
        return users.get(username) == "docs"
    return False


def ApiDoc(app: Sanic):
    """
    add autogenerated Api Docs to the Sanic App
    :param app: the Sanic App
    :return:
    """

    directory = './api_doc/')
    path = '/api_doc'

    bp = Blueprint('api_doc', url_prefix=path)
    bp.static('/', directory)

    @bp.middleware('request')
    @auth.login_required
    def ba_middleware(request: Request):
        """
        inject Basic Authentication for the whole blueprint
        :param request:
        :return:
        """
        pass

    app.blueprint(bp)
```

this works perfectly fine for e.g. the sanic-swagger blueprint.

**Expected behavior**
the blueprint middleware is also applied when serving the static files


**Environment (please complete the following information):**
 - OS: osx
 - Python 3.8.6
 - Sanic 20.9.0



Discussion:
Hi @digitalkaoz 
I was about to reply with a comment like "this is expected, because request middlewares do not apply to static file routes".
However I just did a test, and `app.middleware('request')` decorators _do_ apply to `app.static()` routes.
So this must be a bug in the way blueprints register static routes. Its likely related to the relatively new namespaced middlewares used in blueprints (ie, `named_middleware`) looks like it the `applicable_middlewares` algorithm does not detect static routes within a blueprint as a candidate to apply the middleware to.

This might be intentional, but I doubt it. I'll add a test for it in our test suite, and try to see if there is a clean way to fix it without introducing any breakage or performance decrease here.


Resolving patch:
diff --git a/sanic/app.py b/sanic/app.py
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -676,9 +676,10 @@ def static(
         :param strict_slashes: Instruct :class:`Sanic` to check if the request
             URLs need to terminate with a */*
         :param content_type: user defined content type for header
-        :return: None
+        :return: routes registered on the router
+        :rtype: List[sanic.router.Route]
         """
-        static_register(
+        return static_register(
             self,
             uri,
             file_or_directory,
diff --git a/sanic/blueprints.py b/sanic/blueprints.py
--- a/sanic/blueprints.py
+++ b/sanic/blueprints.py
@@ -143,7 +143,18 @@ def register(self, app, options):
             if _routes:
                 routes += _routes
 
+        # Static Files
+        for future in self.statics:
+            # Prepend the blueprint URI prefix if available
+            uri = url_prefix + future.uri if url_prefix else future.uri
+            _routes = app.static(
+                uri, future.file_or_directory, *future.args, **future.kwargs
+            )
+            if _routes:
+                routes += _routes
+
         route_names = [route.name for route in routes if route]
+
         # Middleware
         for future in self.middlewares:
             if future.args or future.kwargs:
@@ -160,14 +171,6 @@ def register(self, app, options):
         for future in self.exceptions:
             app.exception(*future.args, **future.kwargs)(future.handler)
 
-        # Static Files
-        for future in self.statics:
-            # Prepend the blueprint URI prefix if available
-            uri = url_prefix + future.uri if url_prefix else future.uri
-            app.static(
-                uri, future.file_or_directory, *future.args, **future.kwargs
-            )
-
         # Event listeners
         for event, listeners in self.listeners.items():
             for listener in listeners:
diff --git a/sanic/static.py b/sanic/static.py
--- a/sanic/static.py
+++ b/sanic/static.py
@@ -134,6 +134,8 @@ def register(
                               threshold size to switch to file_stream()
     :param name: user defined name used for url_for
     :param content_type: user defined content type for header
+    :return: registered static routes
+    :rtype: List[sanic.router.Route]
     """
     # If we're not trying to match a file directly,
     # serve from the folder
@@ -155,10 +157,11 @@ def register(
         )
     )
 
-    app.route(
+    _routes, _ = app.route(
         uri,
         methods=["GET", "HEAD"],
         name=name,
         host=host,
         strict_slashes=strict_slashes,
     )(_handler)
+    return _routes


Test patch:
diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py
--- a/tests/test_blueprints.py
+++ b/tests/test_blueprints.py
@@ -735,6 +735,36 @@ def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
     _, response = app.test_client.get("/static/test.file/")
     assert response.status == 200
 
+@pytest.mark.parametrize("file_name", ["test.file"])
+def test_static_blueprintp_mw(app: Sanic, static_file_directory, file_name):
+    current_file = inspect.getfile(inspect.currentframe())
+    with open(current_file, "rb") as file:
+        file.read()
+
+    triggered = False
+
+    bp = Blueprint(name="test_mw", url_prefix="")
+
+    @bp.middleware('request')
+    def bp_mw1(request):
+        nonlocal triggered
+        triggered = True
+
+    bp.static(
+        "/test.file",
+        get_file_path(static_file_directory, file_name),
+        strict_slashes=True,
+        name="static"
+    )
+
+    app.blueprint(bp)
+
+    uri = app.url_for("test_mw.static")
+    assert uri == "/test.file"
+
+    _, response = app.test_client.get("/test.file")
+    assert triggered is True
+
 
 def test_route_handler_add(app: Sanic):
     view = CompositionView()