<h2 dir="auto">The Bug</h2>
<ul dir="auto">
<li><code class="notranslate">flask.views.View</code> does not properly handle conversion of an async <code class="notranslate">dispatch_request</code>, resulting in a coroutine that is never awaited.</li>
<li><code class="notranslate">flask.views.MethodView</code> does not properly handle conversion of async methods, resulting in coroutines that are never awaited.</li>
</ul>
<h2 dir="auto">Replication</h2>
<ol dir="auto">
<li>Create an async <code class="notranslate">dispatch_request</code> method on a <code class="notranslate">View</code>, or a <code class="notranslate">get</code> method on a <code class="notranslate">MethodView</code>.</li>
<li>Add the view to the app using the <code class="notranslate">View.as_view()</code> and <code class="notranslate">app.add_url_rule()</code> functions.</li>
<li>Access the route and observe a crash and 500 status code.</li>
</ol>
<p dir="auto">The following application reproduces the bug.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Flask
from flask.views import View, MethodView

app = Flask(&quot;MyApp&quot;)

class TestAsyncView(View):
    async def dispatch_request(self):
        return 'ASYNC VIEW RESPONSE'

class TestAsyncMethodView(MethodView):
    async def get(self):
        return 'ASYNC METHODVIEW GET RESPONSE'

app.add_url_rule(&quot;/async_view&quot;, view_func=TestAsyncView.as_view(&quot;test_async_view&quot;))
app.add_url_rule('/async_methodview', view_func=TestAsyncMethodView.as_view('test_async_methodview'))

if __name__ == &quot;__main__&quot;:
    app.run(port=8000)"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Flask</span>
<span class="pl-k">from</span> <span class="pl-s1">flask</span>.<span class="pl-s1">views</span> <span class="pl-k">import</span> <span class="pl-v">View</span>, <span class="pl-v">MethodView</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">Flask</span>(<span class="pl-s">"MyApp"</span>)

<span class="pl-k">class</span> <span class="pl-v">TestAsyncView</span>(<span class="pl-v">View</span>):
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">dispatch_request</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">return</span> <span class="pl-s">'ASYNC VIEW RESPONSE'</span>

<span class="pl-k">class</span> <span class="pl-v">TestAsyncMethodView</span>(<span class="pl-v">MethodView</span>):
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">get</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">return</span> <span class="pl-s">'ASYNC METHODVIEW GET RESPONSE'</span>

<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">"/async_view"</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-v">TestAsyncView</span>.<span class="pl-en">as_view</span>(<span class="pl-s">"test_async_view"</span>))
<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/async_methodview'</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-v">TestAsyncMethodView</span>.<span class="pl-en">as_view</span>(<span class="pl-s">'test_async_methodview'</span>))

<span class="pl-k">if</span> <span class="pl-s1">__name__</span> <span class="pl-c1">==</span> <span class="pl-s">"__main__"</span>:
    <span class="pl-s1">app</span>.<span class="pl-en">run</span>(<span class="pl-s1">port</span><span class="pl-c1">=</span><span class="pl-c1">8000</span>)</pre></div>
<p dir="auto">The following error occurs after running <code class="notranslate">curl localhost:8000/async_view</code>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Traceback (most recent call last):
  File &quot;/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py&quot;, line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File &quot;/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py&quot;, line 1519, in full_dispatch_request
    return self.finalize_request(rv)
  File &quot;/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py&quot;, line 1538, in finalize_request
    response = self.make_response(rv)
  File &quot;/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py&quot;, line 1730, in make_response
    raise TypeError(
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a coroutine."><pre class="notranslate"><code class="notranslate">Traceback (most recent call last):
  File "/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py", line 1519, in full_dispatch_request
    return self.finalize_request(rv)
  File "/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py", line 1538, in finalize_request
    response = self.make_response(rv)
  File "/Users/tpansino/NewRelic/Testing/Flask2Bug/flask/src/flask/app.py", line 1730, in make_response
    raise TypeError(
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a coroutine.
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 3.2 Final//EN&quot;&gt;
&lt;title&gt;500 Internal Server Error&lt;/title&gt;
&lt;h1&gt;Internal Server Error&lt;/h1&gt;
&lt;p&gt;The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.&lt;/p&gt;"><pre class="notranslate"><code class="notranslate">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"&gt;
&lt;title&gt;500 Internal Server Error&lt;/title&gt;
&lt;h1&gt;Internal Server Error&lt;/h1&gt;
&lt;p&gt;The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.&lt;/p&gt;
</code></pre></div>
<h2 dir="auto">Expected Behavior</h2>
<ul dir="auto">
<li>Asynchronous <code class="notranslate">dispatch_request</code> functions and methods should either be:
<ul dir="auto">
<li>Converted using <code class="notranslate">async_to_sync</code> before being returned from <code class="notranslate">as_view()</code>.</li>
<li>Executed or awaited in the application when a route is accessed.</li>
</ul>
</li>
</ul>
<h2 dir="auto">Environment</h2>
<ul dir="auto">
<li>Python version: 3.9.5</li>
<li>Flask version: 2.0.1</li>
</ul>
<p dir="auto">Regular views can now be written as async functions, but instances of the <code class="notranslate">View</code> and <code class="notranslate">MethodView</code> classes did not get updated to support this functionality. Consider the following example:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import asyncio
from flask import Flask
from flask.views import View, MethodView

app = Flask(__name__)

class Test(View):
    async def dispatch_request(self):
        await asyncio.sleep(1)
        return 'test'

class Test2(MethodView):
    async def get(self):
        await asyncio.sleep(1)
        return 'test2'

app.add_url_rule('/test', view_func=Test.as_view('test'))
app.add_url_rule('/test2', view_func=Test2.as_view('test2'))"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">asyncio</span>
<span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Flask</span>
<span class="pl-k">from</span> <span class="pl-s1">flask</span>.<span class="pl-s1">views</span> <span class="pl-k">import</span> <span class="pl-v">View</span>, <span class="pl-v">MethodView</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">Flask</span>(<span class="pl-s1">__name__</span>)

<span class="pl-k">class</span> <span class="pl-v">Test</span>(<span class="pl-v">View</span>):
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">dispatch_request</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">await</span> <span class="pl-s1">asyncio</span>.<span class="pl-en">sleep</span>(<span class="pl-c1">1</span>)
        <span class="pl-k">return</span> <span class="pl-s">'test'</span>

<span class="pl-k">class</span> <span class="pl-v">Test2</span>(<span class="pl-v">MethodView</span>):
    <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">get</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">await</span> <span class="pl-s1">asyncio</span>.<span class="pl-en">sleep</span>(<span class="pl-c1">1</span>)
        <span class="pl-k">return</span> <span class="pl-s">'test2'</span>

<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/test'</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-v">Test</span>.<span class="pl-en">as_view</span>(<span class="pl-s">'test'</span>))
<span class="pl-s1">app</span>.<span class="pl-en">add_url_rule</span>(<span class="pl-s">'/test2'</span>, <span class="pl-s1">view_func</span><span class="pl-c1">=</span><span class="pl-v">Test2</span>.<span class="pl-en">as_view</span>(<span class="pl-s">'test2'</span>))</pre></div>
<p dir="auto">The expectation is that you should be able to send requests to <code class="notranslate">/test</code> and <code class="notranslate">/test2</code> and that their respective handlers should be wrapped with the <code class="notranslate">app.ensure_sync()</code> method, as function-based handlers are.</p>
<p dir="auto">Instead sending requests to these URLs cause the following error:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a coroutine."><pre class="notranslate"><code class="notranslate">TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a coroutine.
</code></pre></div>
<p dir="auto">Environment:</p>
<ul dir="auto">
<li>Python version: &gt;= 3.7</li>
<li>Flask version: &gt;= 2</li>
</ul>