<h3 dir="auto">First check</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I added a very descriptive title to this issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I used the GitHub search to find a similar issue and didn't find it.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I searched the FastAPI documentation, with the integrated search.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already searched in Google "How to X in FastAPI" and didn't find any information.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already read and followed all the tutorial in the docs and didn't find an answer.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/samuelcolvin/pydantic">Pydantic</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/swagger-api/swagger-ui">Swagger UI</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/Redocly/redoc">ReDoc</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> After submitting this, I commit to one of:
<ul dir="auto">
<li>Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.</li>
<li>I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.</li>
<li>Implement a Pull Request for a confirmed bug.</li>
</ul>
</li>
</ul>

<h3 dir="auto">Example</h3>
<p dir="auto">Here's a self-contained, <a href="https://stackoverflow.com/help/minimal-reproducible-example" rel="nofollow">minimal, reproducible, example</a> with my use case:</p>

<p dir="auto">server.py</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from time import time

from fastapi import FastAPI, Request, Response

app = FastAPI()


@app.middleware(&quot;http&quot;)
async def middle(request: Request, call_next):
    return await call_next(request)


@app.post(&quot;/&quot;)
async def handler(request: Request):
    print(&quot;processing&quot;)
    request_json = await request.json()

    # Add processing time
    s_time = time()
    count = 0
    while count &lt; 10_000:
        count += 1
    print(f&quot;done: {time() - s_time}&quot;)

    return request_json"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">time</span> <span class="pl-k">import</span> <span class="pl-s1">time</span>

<span class="pl-k">from</span> <span class="pl-s1">fastapi</span> <span class="pl-k">import</span> <span class="pl-v">FastAPI</span>, <span class="pl-v">Request</span>, <span class="pl-v">Response</span>

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


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">middleware</span>(<span class="pl-s">"http"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">middle</span>(<span class="pl-s1">request</span>: <span class="pl-v">Request</span>, <span class="pl-s1">call_next</span>):
    <span class="pl-k">return</span> <span class="pl-k">await</span> <span class="pl-en">call_next</span>(<span class="pl-s1">request</span>)


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">post</span>(<span class="pl-s">"/"</span>)</span>
<span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">handler</span>(<span class="pl-s1">request</span>: <span class="pl-v">Request</span>):
    <span class="pl-en">print</span>(<span class="pl-s">"processing"</span>)
    <span class="pl-s1">request_json</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">request</span>.<span class="pl-en">json</span>()

    <span class="pl-c"># Add processing time</span>
    <span class="pl-s1">s_time</span> <span class="pl-c1">=</span> <span class="pl-en">time</span>()
    <span class="pl-s1">count</span> <span class="pl-c1">=</span> <span class="pl-c1">0</span>
    <span class="pl-k">while</span> <span class="pl-s1">count</span> <span class="pl-c1">&lt;</span> <span class="pl-c1">10_000</span>:
        <span class="pl-s1">count</span> <span class="pl-c1">+=</span> <span class="pl-c1">1</span>
    <span class="pl-en">print</span>(<span class="pl-s">f"done: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">time</span>() <span class="pl-c1">-</span> <span class="pl-s1">s_time</span><span class="pl-kos">}</span></span>"</span>)

    <span class="pl-k">return</span> <span class="pl-s1">request_json</span></pre></div>
<p dir="auto">test_server.py</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from os import getenv
from unittest import TestCase

from requests import Session
from requests.exceptions import HTTPError


class TestModelServer(TestCase):
    def setUp(self):
        host = getenv(&quot;HOST&quot;, &quot;localhost&quot;)
        port = getenv(&quot;PORT&quot;, &quot;8000&quot;)
        self.url = f&quot;http://{host}:{port}&quot;

    def test_post(self):
        with Session() as s:
            for i in range(1000):
                data = [i for i in range(5)]
                resp = s.post(self.url, json=data)
                resp.raise_for_status()
                result = resp.json()
                self.assertEqual(result, data)"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">os</span> <span class="pl-k">import</span> <span class="pl-s1">getenv</span>
<span class="pl-k">from</span> <span class="pl-s1">unittest</span> <span class="pl-k">import</span> <span class="pl-v">TestCase</span>

<span class="pl-k">from</span> <span class="pl-s1">requests</span> <span class="pl-k">import</span> <span class="pl-v">Session</span>
<span class="pl-k">from</span> <span class="pl-s1">requests</span>.<span class="pl-s1">exceptions</span> <span class="pl-k">import</span> <span class="pl-v">HTTPError</span>


<span class="pl-k">class</span> <span class="pl-v">TestModelServer</span>(<span class="pl-v">TestCase</span>):
    <span class="pl-k">def</span> <span class="pl-en">setUp</span>(<span class="pl-s1">self</span>):
        <span class="pl-s1">host</span> <span class="pl-c1">=</span> <span class="pl-en">getenv</span>(<span class="pl-s">"HOST"</span>, <span class="pl-s">"localhost"</span>)
        <span class="pl-s1">port</span> <span class="pl-c1">=</span> <span class="pl-en">getenv</span>(<span class="pl-s">"PORT"</span>, <span class="pl-s">"8000"</span>)
        <span class="pl-s1">self</span>.<span class="pl-s1">url</span> <span class="pl-c1">=</span> <span class="pl-s">f"http://<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">host</span><span class="pl-kos">}</span></span>:<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">port</span><span class="pl-kos">}</span></span>"</span>

    <span class="pl-k">def</span> <span class="pl-en">test_post</span>(<span class="pl-s1">self</span>):
        <span class="pl-k">with</span> <span class="pl-v">Session</span>() <span class="pl-k">as</span> <span class="pl-s1">s</span>:
            <span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">in</span> <span class="pl-en">range</span>(<span class="pl-c1">1000</span>):
                <span class="pl-s1">data</span> <span class="pl-c1">=</span> [<span class="pl-s1">i</span> <span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">in</span> <span class="pl-en">range</span>(<span class="pl-c1">5</span>)]
                <span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">s</span>.<span class="pl-en">post</span>(<span class="pl-s1">self</span>.<span class="pl-s1">url</span>, <span class="pl-s1">json</span><span class="pl-c1">=</span><span class="pl-s1">data</span>)
                <span class="pl-s1">resp</span>.<span class="pl-en">raise_for_status</span>()
                <span class="pl-s1">result</span> <span class="pl-c1">=</span> <span class="pl-s1">resp</span>.<span class="pl-en">json</span>()
                <span class="pl-s1">self</span>.<span class="pl-en">assertEqual</span>(<span class="pl-s1">result</span>, <span class="pl-s1">data</span>)</pre></div>
<p dir="auto">docker-compose.yml</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="version: &quot;3&quot;
services:
  test:
    image: python:3.8.3
    environment:
      HOST: app
    command:
      - /bin/sh
      - -c
      - |
        set -eu
        cd /app
        pip install --no-cache-dir requests
        # Wait until app is ready
        until $$(curl -s $$HOST:8000/metrics &gt; /dev/null); do
          echo &quot;app not ready&quot;
          sleep 5
        done
        python -m unittest -v test_server.py
    volumes:
      - ./test_server.py:/app/test_server.py
  app:
    image: python:3.8.3
    command:
      - /bin/sh
      - -c
      - |
        cd /app
        pip install --no-cache-dir fastapi uvicorn
        uvicorn --host &quot;0.0.0.0&quot; --workers 1 server:app
    volumes:
      - ./server.py:/app/server.py"><pre class="notranslate"><span class="pl-ent">version</span>: <span class="pl-s"><span class="pl-pds">"</span>3<span class="pl-pds">"</span></span>
<span class="pl-ent">services</span>:
  <span class="pl-ent">test</span>:
    <span class="pl-ent">image</span>: <span class="pl-s">python:3.8.3</span>
    <span class="pl-ent">environment</span>:
      <span class="pl-ent">HOST</span>: <span class="pl-s">app</span>
    <span class="pl-ent">command</span>:
      - <span class="pl-s">/bin/sh</span>
      - <span class="pl-s">-c</span>
      - <span class="pl-s">|</span>
<span class="pl-s">        set -eu</span>
<span class="pl-s">        cd /app</span>
<span class="pl-s">        pip install --no-cache-dir requests</span>
<span class="pl-s">        # Wait until app is ready</span>
<span class="pl-s">        until $$(curl -s $$HOST:8000/metrics &gt; /dev/null); do</span>
<span class="pl-s">          echo "app not ready"</span>
<span class="pl-s">          sleep 5</span>
<span class="pl-s">        done</span>
<span class="pl-s">        python -m unittest -v test_server.py</span>
<span class="pl-s"></span>    <span class="pl-ent">volumes</span>:
      - <span class="pl-s">./test_server.py:/app/test_server.py</span>
  <span class="pl-ent">app</span>:
    <span class="pl-ent">image</span>: <span class="pl-s">python:3.8.3</span>
    <span class="pl-ent">command</span>:
      - <span class="pl-s">/bin/sh</span>
      - <span class="pl-s">-c</span>
      - <span class="pl-s">|</span>
<span class="pl-s">        cd /app</span>
<span class="pl-s">        pip install --no-cache-dir fastapi uvicorn</span>
<span class="pl-s">        uvicorn --host "0.0.0.0" --workers 1 server:app</span>
<span class="pl-s"></span>    <span class="pl-ent">volumes</span>:
      - <span class="pl-s">./server.py:/app/server.py</span></pre></div>
<h3 dir="auto">Description</h3>

<ul dir="auto">
<li>Put all 3 files in the same directory and run <code class="notranslate">docker-compose up --abort-on-container-exit --build</code></li>
<li><code class="notranslate">test_server.py</code> hangs after completing several POST requests.</li>
<li>But I expected it to run to completion.</li>
</ul>
<p dir="auto">If I remove the middleware, tests complete as expected. It also seems to work if I close the connection from client side after every request, (ie. use a new requests.Session object).</p>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>OS: Docker on macOS</li>
<li>FastAPI Version: 0.59.0</li>
</ul>
<p dir="auto">To know the FastAPI version use:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="python -c &quot;import fastapi; print(fastapi.__version__)&quot;"><pre class="notranslate">python -c <span class="pl-s"><span class="pl-pds">"</span>import fastapi; print(fastapi.__version__)<span class="pl-pds">"</span></span></pre></div>
<ul dir="auto">
<li>Python version: 3.8.3</li>
</ul>
<p dir="auto">To know the Python version use:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="python --version"><pre class="notranslate">python --version</pre></div>
<h3 dir="auto">Additional context</h3>
<h3 dir="auto">First Check</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I added a very descriptive title to this issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I used the GitHub search to find a similar issue and didn't find it.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I searched the FastAPI documentation, with the integrated search.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already searched in Google "How to X in FastAPI" and didn't find any information.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already read and followed all the tutorial in the docs and didn't find an answer.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/samuelcolvin/pydantic">Pydantic</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/swagger-api/swagger-ui">Swagger UI</a>.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I already checked if it is not related to FastAPI but to <a href="https://github.com/Redocly/redoc">ReDoc</a>.</li>
</ul>
<h3 dir="auto">Commit to Help</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I commit to help with one of those options <g-emoji class="g-emoji" alias="point_up_2" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f446.png">👆</g-emoji></li>
</ul>
<h3 dir="auto">Example Code</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="router.add_api_route(
    &quot;/clear&quot;,
    clear,
    methods=[&quot;POST&quot;, &quot;DELETE&quot;]
)"><pre class="notranslate"><span class="pl-s1">router</span>.<span class="pl-en">add_api_route</span>(
    <span class="pl-s">"/clear"</span>,
    <span class="pl-s1">clear</span>,
    <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">"POST"</span>, <span class="pl-s">"DELETE"</span>]
)</pre></div>
<h3 dir="auto">Description</h3>
<p dir="auto">Seems to be caused by <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1160082813" data-permission-text="Title is private" data-url="https://github.com/tiangolo/fastapi/issues/4650" data-hovercard-type="pull_request" data-hovercard-url="/tiangolo/fastapi/pull/4650/hovercard" href="https://github.com/tiangolo/fastapi/pull/4650">#4650</a>.</p>
<p dir="auto">The new <code class="notranslate">generate_unique_id()</code> function uses <code class="notranslate">list(route.methods)[0].lower()</code> as suffix for the <code class="notranslate">operation_id</code>. Therefore, in my example, both post and delete endpoints get <code class="notranslate">_post</code> suffix for operation_id, causing it to no longer be unique.</p>
<p dir="auto">It then issues a "UserWarning: Duplicate Operation ID"</p>
<h3 dir="auto">Operating System</h3>
<p dir="auto">Windows</p>
<h3 dir="auto">Operating System Details</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">FastAPI Version</h3>
<p dir="auto">0.75.0</p>
<h3 dir="auto">Python Version</h3>
<p dir="auto">3.10.2</p>
<h3 dir="auto">Additional Context</h3>
<p dir="auto"><em>No response</em></p>