mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-27 08:10:57 -05:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63a5ffcf57 | ||
|
|
da1c67338f | ||
|
|
46a509649d | ||
|
|
1d416c4c53 | ||
|
|
89095eba4f | ||
|
|
5a63f660de | ||
|
|
9c483505e3 | ||
|
|
ad0e923fed | ||
|
|
fc717f84ff | ||
|
|
b1204a9b62 | ||
|
|
16630bf54d | ||
|
|
50ea75ae98 | ||
|
|
a0852e2f53 | ||
|
|
fa74093440 | ||
|
|
bcd9ab95e1 | ||
|
|
d537ee93d7 | ||
|
|
1c93d5523a | ||
|
|
f92f87d379 | ||
|
|
57141ccac4 | ||
|
|
46b903f70b | ||
|
|
fdbd48be5f | ||
|
|
5f67ac6fd6 | ||
|
|
ba5310f731 | ||
|
|
f57ccd8ef3 | ||
|
|
c040e3602a | ||
|
|
86d4073632 | ||
|
|
59208e4ddc | ||
|
|
0781a91d19 | ||
|
|
41735d2de9 | ||
|
|
a0c717d784 | ||
|
|
0ed9ca78bc | ||
|
|
678d35994a | ||
|
|
a7edd0b80d | ||
|
|
f36d2e2b2b | ||
|
|
5f0e095689 |
5
.flake8
5
.flake8
@@ -1,5 +0,0 @@
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
select = C,E,F,W,B,B9
|
||||
ignore = E203, E501, W503
|
||||
exclude = __init__.py
|
||||
@@ -18,3 +18,4 @@ uz: 4883
|
||||
sv: 5146
|
||||
he: 5157
|
||||
ta: 5434
|
||||
ar: 3349
|
||||
|
||||
2
.github/workflows/preview-docs.yml
vendored
2
.github/workflows/preview-docs.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download Artifact Docs
|
||||
uses: dawidd6/action-download-artifact@v2.24.0
|
||||
uses: dawidd6/action-download-artifact@v2.24.2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
workflow: build-docs.yml
|
||||
|
||||
2
.github/workflows/smokeshow.yml
vendored
2
.github/workflows/smokeshow.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
- run: pip install smokeshow
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
- uses: dawidd6/action-download-artifact@v2.24.2
|
||||
with:
|
||||
workflow: test.yml
|
||||
commit: ${{ github.event.workflow_run.head_sha }}
|
||||
|
||||
@@ -18,19 +18,12 @@ repos:
|
||||
args:
|
||||
- --py3-plus
|
||||
- --keep-runtime-typing
|
||||
- repo: https://github.com/PyCQA/autoflake
|
||||
rev: v1.7.7
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.114
|
||||
hooks:
|
||||
- id: autoflake
|
||||
- id: ruff
|
||||
args:
|
||||
- --recursive
|
||||
- --in-place
|
||||
- --remove-all-unused-imports
|
||||
- --remove-unused-variables
|
||||
- --expand-star-imports
|
||||
- --exclude
|
||||
- __init__.py
|
||||
- --remove-duplicate-keys
|
||||
- --fix
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.10.1
|
||||
hooks:
|
||||
|
||||
@@ -427,7 +427,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -447,7 +447,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -446,7 +446,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -169,7 +169,7 @@ Mit **FastAPI** bekommen Sie viele von **Starlette**'s Funktionen (da FastAPI nu
|
||||
* **WebSocket**-Unterstützung.
|
||||
* Hintergrundaufgaben im selben Prozess.
|
||||
* Ereignisse für das Starten und Herunterfahren.
|
||||
* Testclient basierend auf `requests`.
|
||||
* Testclient basierend auf HTTPX.
|
||||
* **CORS**, GZip, statische Dateien, Antwortfluss.
|
||||
* **Sitzungs und Cookie** Unterstützung.
|
||||
* 100% Testabdeckung.
|
||||
|
||||
@@ -445,7 +445,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Async Tests
|
||||
|
||||
You have already seen how to test your **FastAPI** applications using the provided `TestClient`, but with it, you can't test or run any other `async` function in your (synchronous) pytest functions.
|
||||
You have already seen how to test your **FastAPI** applications using the provided `TestClient`. Up to now, you have only seen how to write synchronous tests, without using `async` functions.
|
||||
|
||||
Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library.
|
||||
|
||||
@@ -8,7 +8,7 @@ Let's look at how we can make that work.
|
||||
|
||||
## pytest.mark.anyio
|
||||
|
||||
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. Anyio provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
|
||||
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
|
||||
|
||||
## HTTPX
|
||||
|
||||
@@ -16,13 +16,7 @@ Even if your **FastAPI** application uses normal `def` functions instead of `asy
|
||||
|
||||
The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions.
|
||||
|
||||
Luckily there's a nice alternative, called <a href="https://www.python-httpx.org/" class="external-link" target="_blank">HTTPX</a>.
|
||||
|
||||
HTTPX is an HTTP client for Python 3 that allows us to query our FastAPI application similarly to how we did it with the `TestClient`.
|
||||
|
||||
If you're familiar with the <a href="https://requests.readthedocs.io/en/master/" class="external-link" target="_blank">Requests</a> library, you'll find that the API of HTTPX is almost identical.
|
||||
|
||||
The important difference for us is that with HTTPX we are not limited to synchronous, but can also make asynchronous requests.
|
||||
The `TestClient` is based on <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, and luckily, we can use it directly to test the API.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -85,7 +79,7 @@ This is the equivalent to:
|
||||
response = client.get('/')
|
||||
```
|
||||
|
||||
that we used to make our requests with the `TestClient`.
|
||||
...that we used to make our requests with the `TestClient`.
|
||||
|
||||
!!! tip
|
||||
Note that we're using async/await with the new `AsyncClient` - the request is asynchronous.
|
||||
|
||||
@@ -50,7 +50,7 @@ It could be just one or two lines of code, like:
|
||||
|
||||
```Python
|
||||
callback_url = "https://example.com/api/v1/invoices/events/"
|
||||
requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
```
|
||||
|
||||
But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc.
|
||||
@@ -64,7 +64,7 @@ This example doesn't implement the callback itself (that could be just a line of
|
||||
!!! tip
|
||||
The actual callback is just an HTTP request.
|
||||
|
||||
When implementing the callback yourself, you could use something like <a href="https://www.encode.io/httpx/" class="external-link" target="_blank">HTTPX</a> or <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
|
||||
When implementing the callback yourself, you could use something like <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> or <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
|
||||
|
||||
## Write the callback documentation code
|
||||
|
||||
|
||||
@@ -112,17 +112,15 @@ In WebSocket endpoints you can import from `fastapi` and use:
|
||||
|
||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
```Python hl_lines="58-65 68-83"
|
||||
```Python hl_lines="66-77 76-91"
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
|
||||
As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`.
|
||||
|
||||
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
|
||||
|
||||
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
|
||||
|
||||
### Try the WebSockets with dependencies
|
||||
|
||||
If your file is named `main.py`, run your application with:
|
||||
|
||||
@@ -367,7 +367,7 @@ It has:
|
||||
* WebSocket support.
|
||||
* In-process background tasks.
|
||||
* Startup and shutdown events.
|
||||
* Test client built on requests.
|
||||
* Test client built on HTTPX.
|
||||
* CORS, GZip, Static Files, Streaming responses.
|
||||
* Session and Cookie support.
|
||||
* 100% test coverage.
|
||||
|
||||
@@ -166,7 +166,7 @@ With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Sta
|
||||
* **WebSocket** support.
|
||||
* In-process background tasks.
|
||||
* Startup and shutdown events.
|
||||
* Test client built on `requests`.
|
||||
* Test client built on HTTPX.
|
||||
* **CORS**, GZip, Static Files, Streaming responses.
|
||||
* **Session and Cookie** support.
|
||||
* 100% test coverage.
|
||||
|
||||
@@ -47,7 +47,7 @@ You can:
|
||||
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Follow me on **GitHub**</a>.
|
||||
* See other Open Source projects I have created that could help you.
|
||||
* Follow me to see when I create a new Open Source project.
|
||||
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a>.
|
||||
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a> or <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>.
|
||||
* Tell me how you use FastAPI (I love to hear that).
|
||||
* Hear when I make announcements or release new tools.
|
||||
* You can also <a href="https://twitter.com/fastapi" class="external-link" target="_blank">follow @fastapi on Twitter</a> (a separate account).
|
||||
@@ -67,13 +67,54 @@ I love to hear about how **FastAPI** is being used, what you have liked in it, i
|
||||
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vote for **FastAPI** in Slant</a>.
|
||||
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Vote for **FastAPI** in AlternativeTo</a>.
|
||||
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Say you use **FastAPI** on StackShare</a>.
|
||||
|
||||
## Help others with issues in GitHub
|
||||
|
||||
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓
|
||||
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times those issues are questions that you might already know the answer for. 🤓
|
||||
|
||||
If you are helping a lot of people with issues, you might become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉
|
||||
|
||||
Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗
|
||||
|
||||
The idea is for the **FastAPI** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.
|
||||
|
||||
---
|
||||
|
||||
Here's how to help others with issues:
|
||||
|
||||
### Understand the question
|
||||
|
||||
* Check if you can understand what is the **purpose** and use case of the person asking.
|
||||
|
||||
* Then check if the question (the vast majority are questions) is **clear**.
|
||||
|
||||
* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.
|
||||
|
||||
* If you can't understand the question, ask for more **details**.
|
||||
|
||||
### Reproduce the problem
|
||||
|
||||
For most of the cases and most of the questions there's something related to the person's **original code**.
|
||||
|
||||
In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.
|
||||
|
||||
* You can ask them to provide a <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">minimal, reproducible, example</a>, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.
|
||||
|
||||
* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.
|
||||
|
||||
### Suggest solutions
|
||||
|
||||
* After being able to understand the question, you can give them a possible **answer**.
|
||||
|
||||
* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.
|
||||
|
||||
### Ask to close
|
||||
|
||||
If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸
|
||||
|
||||
* Now you can ask them, if that solved their problem, to **close the issue**.
|
||||
|
||||
## Watch the GitHub repository
|
||||
|
||||
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀
|
||||
@@ -91,6 +132,57 @@ You can <a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="e
|
||||
|
||||
**Note**: if you create an issue, then I'm going to ask you to also help others. 😉
|
||||
|
||||
## Review Pull Requests
|
||||
|
||||
You can help me review pull requests from others.
|
||||
|
||||
Again, please try your best to be kind. 🤗
|
||||
|
||||
---
|
||||
|
||||
Here's what to have in mind and how to review a pull request:
|
||||
|
||||
### Understand the problem
|
||||
|
||||
* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in an issue.
|
||||
|
||||
* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.
|
||||
|
||||
### Don't worry about style
|
||||
|
||||
* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.
|
||||
|
||||
* Also don't worry about style rules, there are already automatized tools checking that.
|
||||
|
||||
And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.
|
||||
|
||||
### Check the code
|
||||
|
||||
* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.
|
||||
|
||||
* Then **comment** saying that you did that, that's how I will know you really checked it.
|
||||
|
||||
!!! info
|
||||
Unfortunately, I can't simply trust PRs that just have several approvals.
|
||||
|
||||
Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
|
||||
|
||||
So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
|
||||
|
||||
* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
|
||||
|
||||
### Tests
|
||||
|
||||
* Help me check that the PR has **tests**.
|
||||
|
||||
* Check that the tests **fail** before the PR. 🚨
|
||||
|
||||
* Then check that the tests **pass** after the PR. ✅
|
||||
|
||||
* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.
|
||||
|
||||
* Then also comment what you tried, that way I'll know that you checked it. 🤓
|
||||
|
||||
## Create a Pull Request
|
||||
|
||||
You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example:
|
||||
@@ -102,7 +194,25 @@ You can [contribute](contributing.md){.internal-link target=_blank} to the sourc
|
||||
* You can also help to review the translations created by others.
|
||||
* To propose new documentation sections.
|
||||
* To fix an existing issue/bug.
|
||||
* Make sure to add tests.
|
||||
* To add a new feature.
|
||||
* Make sure to add tests.
|
||||
* Make sure to add documentation if it's relevant.
|
||||
|
||||
## Help Maintain FastAPI
|
||||
|
||||
Help me maintain **FastAPI**! 🤓
|
||||
|
||||
There's a lot of work to do, and for most of it, **YOU** can do it.
|
||||
|
||||
The main tasks that you can do right now are:
|
||||
|
||||
* [Help others with issues in GitHub](#help-others-with-issues-in-github){.internal-link target=_blank} (see the section above).
|
||||
* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above).
|
||||
|
||||
Those two tasks are what **consume time the most**. That's the main work of maintaining FastAPI.
|
||||
|
||||
If you can help me with that, **you are helping me maintain FastAPI** and making sure it keeps **advancing faster and better**. 🚀
|
||||
|
||||
## Join the chat
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -444,7 +444,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
## Latest Changes
|
||||
|
||||
|
||||
## 0.87.0
|
||||
|
||||
Highlights of this release:
|
||||
|
||||
* [Upgraded Starlette](https://github.com/encode/starlette/releases/tag/0.21.0)
|
||||
* Now the `TestClient` is based on HTTPX instead of Requests. 🚀
|
||||
* There are some possible **breaking changes** in the `TestClient` usage, but [@Kludex](https://github.com/Kludex) built [bump-testclient](https://github.com/Kludex/bump-testclient) to help you automatize migrating your tests. Make sure you are using Git and that you can undo any unnecessary changes (false positive changes, etc) before using `bump-testclient`.
|
||||
* New [WebSocketException (and docs)](https://fastapi.tiangolo.com/advanced/websockets/#using-depends-and-others), re-exported from Starlette.
|
||||
* Upgraded and relaxed dependencies for package extras `all` (including new Uvicorn version), when you install `"fastapi[all]"`.
|
||||
* New docs about how to [**Help Maintain FastAPI**](https://fastapi.tiangolo.com/help-fastapi/#help-maintain-fastapi).
|
||||
|
||||
### Features
|
||||
|
||||
* ⬆️ Upgrade and relax dependencies for extras "all". PR [#5634](https://github.com/tiangolo/fastapi/pull/5634) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✨ Re-export Starlette's `WebSocketException` and add it to docs. PR [#5629](https://github.com/tiangolo/fastapi/pull/5629) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Update references to Requests for tests to HTTPX, and add HTTPX to extras. PR [#5628](https://github.com/tiangolo/fastapi/pull/5628) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Upgrade Starlette to `0.21.0`, including the new [`TestClient` based on HTTPX](https://github.com/encode/starlette/releases/tag/0.21.0). PR [#5471](https://github.com/tiangolo/fastapi/pull/5471) by [@pawelrubin](https://github.com/pawelrubin).
|
||||
|
||||
### Docs
|
||||
|
||||
* ✏️ Tweak Help FastAPI from PR review after merging. PR [#5633](https://github.com/tiangolo/fastapi/pull/5633) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ✏️ Clarify docs on CORS. PR [#5627](https://github.com/tiangolo/fastapi/pull/5627) by [@paxcodes](https://github.com/paxcodes).
|
||||
* 📝 Update Help FastAPI: Help Maintain FastAPI. PR [#5632](https://github.com/tiangolo/fastapi/pull/5632) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Fix highlight lines for Japanese translation for `docs/tutorial/query-params.md`. PR [#2969](https://github.com/tiangolo/fastapi/pull/2969) by [@ftnext](https://github.com/ftnext).
|
||||
* 🌐 Add French translation for `docs/fr/docs/advanced/additional-status-code.md`. PR [#5477](https://github.com/tiangolo/fastapi/pull/5477) by [@axel584](https://github.com/axel584).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/request-forms-and-files.md`. PR [#5579](https://github.com/tiangolo/fastapi/pull/5579) by [@batlopes](https://github.com/batlopes).
|
||||
* 🌐 Add Japanese translation for `docs/ja/docs/advanced/websockets.md`. PR [#4983](https://github.com/tiangolo/fastapi/pull/4983) by [@xryuseix](https://github.com/xryuseix).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✨ Use Ruff for linting. PR [#5630](https://github.com/tiangolo/fastapi/pull/5630) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🛠 Add Arabic issue number to Notify Translations GitHub Action. PR [#5610](https://github.com/tiangolo/fastapi/pull/5610) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2. PR [#5609](https://github.com/tiangolo/fastapi/pull/5609) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.1. PR [#5603](https://github.com/tiangolo/fastapi/pull/5603) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 📝 Update coverage badge to use Samuel Colvin's Smokeshow. PR [#5585](https://github.com/tiangolo/fastapi/pull/5585) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.86.0
|
||||
|
||||
### Features
|
||||
|
||||
@@ -57,7 +57,7 @@ The following arguments are supported:
|
||||
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
|
||||
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`.
|
||||
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
|
||||
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for CORS requests.
|
||||
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">simple CORS requests</a>.
|
||||
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins` cannot be set to `['*']` for credentials to be allowed, origins must be specified.
|
||||
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
|
||||
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`.
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testing **FastAPI** applications is easy and enjoyable.
|
||||
|
||||
It is based on <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>, so it's very familiar and intuitive.
|
||||
It is based on <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, which in turn is designed based on Requests, so it's very familiar and intuitive.
|
||||
|
||||
With it, you can use <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> directly with **FastAPI**.
|
||||
|
||||
## Using `TestClient`
|
||||
|
||||
!!! info
|
||||
To use `TestClient`, first install <a href="https://github.com/psf/requests" class="external-link" target="_blank">`requests`</a>.
|
||||
To use `TestClient`, first install <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
|
||||
|
||||
E.g. `pip install requests`.
|
||||
E.g. `pip install httpx`.
|
||||
|
||||
Import `TestClient`.
|
||||
|
||||
@@ -19,7 +19,7 @@ Create a `TestClient` by passing your **FastAPI** application to it.
|
||||
|
||||
Create functions with a name that starts with `test_` (this is standard `pytest` conventions).
|
||||
|
||||
Use the `TestClient` object the same way as you do with `requests`.
|
||||
Use the `TestClient` object the same way as you do with `httpx`.
|
||||
|
||||
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
|
||||
|
||||
@@ -130,7 +130,7 @@ You could then update `test_main.py` with the extended tests:
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `requests`.
|
||||
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `httpx`, or even how to do it with `requests`, as HTTPX's design is based on Requests' design.
|
||||
|
||||
Then you just do the same in your tests.
|
||||
|
||||
@@ -142,7 +142,7 @@ E.g.:
|
||||
* To pass *headers*, use a `dict` in the `headers` parameter.
|
||||
* For *cookies*, a `dict` in the `cookies` parameter.
|
||||
|
||||
For more information about how to pass data to the backend (using `requests` or the `TestClient`) check the <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests documentation</a>.
|
||||
For more information about how to pass data to the backend (using `httpx` or the `TestClient`) check the <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a>.
|
||||
|
||||
!!! info
|
||||
Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models.
|
||||
|
||||
@@ -167,7 +167,7 @@ Con **FastAPI** obtienes todas las características de **Starlette** (porque Fas
|
||||
* Soporte para **GraphQL**.
|
||||
* <abbr title="En español: tareas que se ejecutan en el fondo, sin frenar requests, en el mismo proceso. En ingles: In-process background tasks">Tareas en background</abbr>.
|
||||
* Eventos de startup y shutdown.
|
||||
* Cliente de pruebas construido con `requests`.
|
||||
* Cliente de pruebas construido con HTTPX.
|
||||
* **CORS**, GZip, Static Files, Streaming responses.
|
||||
* Soporte para **Session and Cookie**.
|
||||
* Cobertura de pruebas al 100%.
|
||||
|
||||
@@ -418,7 +418,7 @@ Para un ejemplo más completo que incluye más características ve el <a href="h
|
||||
* Muchas características extra (gracias a Starlette) como:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* pruebas extremadamente fáciles con `requests` y `pytest`
|
||||
* pruebas extremadamente fáciles con HTTPX y `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...y mucho más.
|
||||
@@ -438,7 +438,7 @@ Usadas por Pydantic:
|
||||
|
||||
Usados por Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Requerido si quieres usar el `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Requerido si quieres usar el `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si quieres usar la configuración por defecto de templates.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Requerido si quieres dar soporte a <abbr title="convertir el string que viene de un HTTP request a datos de Python">"parsing"</abbr> de formularios, con `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Requerido para dar soporte a `SessionMiddleware`.
|
||||
|
||||
@@ -421,7 +421,7 @@ item: Item
|
||||
* قابلیتهای اضافی دیگر (بر اساس Starlette) شامل:
|
||||
* **<abbr title="WebSocket">وبسوکت</abbr>**
|
||||
* **GraphQL**
|
||||
* تستهای خودکار آسان مبتنی بر `requests` و `pytest`
|
||||
* تستهای خودکار آسان مبتنی بر HTTPX و `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* و موارد بیشمار دیگر.
|
||||
@@ -441,7 +441,7 @@ item: Item
|
||||
|
||||
استفاده شده توسط Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - در صورتی که میخواهید از `TestClient` استفاده کنید.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - در صورتی که میخواهید از `TestClient` استفاده کنید.
|
||||
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - در صورتی که میخواهید از `FileResponse` و `StaticFiles` استفاده کنید.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - در صورتی که بخواهید از پیکربندی پیشفرض برای قالبها استفاده کنید.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - در صورتی که بخواهید با استفاده از `request.form()` از قابلیت <abbr title="تبدیل رشته متنی موجود در درخواست HTTP به انواع داده پایتون">"تجزیه (parse)"</abbr> فرم استفاده کنید.
|
||||
|
||||
240
docs/fr/docs/advanced/additional-responses.md
Normal file
240
docs/fr/docs/advanced/additional-responses.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Réponses supplémentaires dans OpenAPI
|
||||
|
||||
!!! Attention
|
||||
Ceci concerne un sujet plutôt avancé.
|
||||
|
||||
Si vous débutez avec **FastAPI**, vous n'en aurez peut-être pas besoin.
|
||||
|
||||
Vous pouvez déclarer des réponses supplémentaires, avec des codes HTTP, des types de médias, des descriptions, etc.
|
||||
|
||||
Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles apparaîtront donc également dans la documentation de l'API.
|
||||
|
||||
Mais pour ces réponses supplémentaires, vous devez vous assurer de renvoyer directement une `Response` comme `JSONResponse`, avec votre code HTTP et votre contenu.
|
||||
|
||||
## Réponse supplémentaire avec `model`
|
||||
|
||||
Vous pouvez ajouter à votre décorateur de *paramètre de chemin* un paramètre `responses`.
|
||||
|
||||
Il prend comme valeur un `dict` dont les clés sont des codes HTTP pour chaque réponse, comme `200`, et la valeur de ces clés sont d'autres `dict` avec des informations pour chacun d'eux.
|
||||
|
||||
Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modèle Pydantic, tout comme `response_model`.
|
||||
|
||||
**FastAPI** prendra ce modèle, générera son schéma JSON et l'inclura au bon endroit dans OpenAPI.
|
||||
|
||||
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
|
||||
|
||||
```Python hl_lines="18 22"
|
||||
{!../../../docs_src/additional_responses/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! Remarque
|
||||
Gardez à l'esprit que vous devez renvoyer directement `JSONResponse`.
|
||||
|
||||
!!! Info
|
||||
La clé `model` ne fait pas partie d'OpenAPI.
|
||||
|
||||
**FastAPI** prendra le modèle Pydantic à partir de là, générera le `JSON Schema` et le placera au bon endroit.
|
||||
|
||||
Le bon endroit est :
|
||||
|
||||
* Dans la clé `content`, qui a pour valeur un autre objet JSON (`dict`) qui contient :
|
||||
* Une clé avec le type de support, par ex. `application/json`, qui contient comme valeur un autre objet JSON, qui contient :
|
||||
* Une clé `schema`, qui a pour valeur le schéma JSON du modèle, voici le bon endroit.
|
||||
* **FastAPI** ajoute ici une référence aux schémas JSON globaux à un autre endroit de votre OpenAPI au lieu de l'inclure directement. De cette façon, d'autres applications et clients peuvent utiliser ces schémas JSON directement, fournir de meilleurs outils de génération de code, etc.
|
||||
|
||||
Les réponses générées au format OpenAPI pour cette *opération de chemin* seront :
|
||||
|
||||
```JSON hl_lines="3-12"
|
||||
{
|
||||
"responses": {
|
||||
"404": {
|
||||
"description": "Additional Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Item"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Les schémas sont référencés à un autre endroit du modèle OpenAPI :
|
||||
|
||||
```JSON hl_lines="4-16"
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Message": {
|
||||
"title": "Message",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"title": "Message",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"title": "Item",
|
||||
"required": [
|
||||
"id",
|
||||
"value"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"title": "Id",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"title": "Value",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": [
|
||||
"loc",
|
||||
"msg",
|
||||
"type"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"msg": {
|
||||
"title": "Message",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"title": "Error Type",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Types de médias supplémentaires pour la réponse principale
|
||||
|
||||
Vous pouvez utiliser ce même paramètre `responses` pour ajouter différents types de médias pour la même réponse principale.
|
||||
|
||||
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *opération de chemin* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
|
||||
|
||||
```Python hl_lines="19-24 28"
|
||||
{!../../../docs_src/additional_responses/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! Remarque
|
||||
Notez que vous devez retourner l'image en utilisant directement un `FileResponse`.
|
||||
|
||||
!!! Info
|
||||
À moins que vous ne spécifiiez explicitement un type de média différent dans votre paramètre `responses`, FastAPI supposera que la réponse a le même type de média que la classe de réponse principale (par défaut `application/json`).
|
||||
|
||||
Mais si vous avez spécifié une classe de réponse personnalisée avec `None` comme type de média, FastAPI utilisera `application/json` pour toute réponse supplémentaire associée à un modèle.
|
||||
|
||||
## Combinaison d'informations
|
||||
|
||||
Vous pouvez également combiner des informations de réponse provenant de plusieurs endroits, y compris les paramètres `response_model`, `status_code` et `responses`.
|
||||
|
||||
Vous pouvez déclarer un `response_model`, en utilisant le code HTTP par défaut `200` (ou un code personnalisé si vous en avez besoin), puis déclarer des informations supplémentaires pour cette même réponse dans `responses`, directement dans le schéma OpenAPI.
|
||||
|
||||
**FastAPI** conservera les informations supplémentaires des `responses` et les combinera avec le schéma JSON de votre modèle.
|
||||
|
||||
Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui utilise un modèle Pydantic et a une `description` personnalisée.
|
||||
|
||||
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
|
||||
|
||||
```Python hl_lines="20-31"
|
||||
{!../../../docs_src/additional_responses/tutorial003.py!}
|
||||
```
|
||||
|
||||
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
## Combinez les réponses prédéfinies et les réponses personnalisées
|
||||
|
||||
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *paramètre de chemin*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *opération de chemin*.
|
||||
|
||||
Dans ces cas, vous pouvez utiliser la technique Python "d'affection par décomposition" (appelé _unpacking_ en anglais) d'un `dict` avec `**dict_to_unpack` :
|
||||
|
||||
``` Python
|
||||
old_dict = {
|
||||
"old key": "old value",
|
||||
"second old key": "second old value",
|
||||
}
|
||||
new_dict = {**old_dict, "new key": "new value"}
|
||||
```
|
||||
|
||||
Ici, `new_dict` contiendra toutes les paires clé-valeur de `old_dict` plus la nouvelle paire clé-valeur :
|
||||
|
||||
``` Python
|
||||
{
|
||||
"old key": "old value",
|
||||
"second old key": "second old value",
|
||||
"new key": "new value",
|
||||
}
|
||||
```
|
||||
|
||||
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *paramètres de chemin* et les combiner avec des réponses personnalisées supplémentaires.
|
||||
|
||||
Par exemple:
|
||||
|
||||
```Python hl_lines="13-17 26"
|
||||
{!../../../docs_src/additional_responses/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Plus d'informations sur les réponses OpenAPI
|
||||
|
||||
Pour voir exactement ce que vous pouvez inclure dans les réponses, vous pouvez consulter ces sections dans la spécification OpenAPI :
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" class="external-link" target="_blank">Objet Responses de OpenAPI </a>, il inclut le `Response Object`.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" class="external-link" target="_blank">Objet Response de OpenAPI </a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.
|
||||
37
docs/fr/docs/advanced/additional-status-codes.md
Normal file
37
docs/fr/docs/advanced/additional-status-codes.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Codes HTTP supplémentaires
|
||||
|
||||
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *chemin d'accès* à l'intérieur de cette `JSONResponse`.
|
||||
|
||||
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *chemin d'accès*.
|
||||
|
||||
## Codes HTTP supplémentaires
|
||||
|
||||
Si vous souhaitez renvoyer des codes HTTP supplémentaires en plus du code principal, vous pouvez le faire en renvoyant directement une `Response`, comme une `JSONResponse`, et en définissant directement le code HTTP supplémentaire.
|
||||
|
||||
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 "OK" en cas de succès.
|
||||
|
||||
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 "Créé".
|
||||
|
||||
Pour y parvenir, importez `JSONResponse` et renvoyez-y directement votre contenu, en définissant le `status_code` que vous souhaitez :
|
||||
|
||||
```Python hl_lines="4 25"
|
||||
{!../../../docs_src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! Attention
|
||||
Lorsque vous renvoyez une `Response` directement, comme dans l'exemple ci-dessus, elle sera renvoyée directement.
|
||||
|
||||
Elle ne sera pas sérialisée avec un modèle.
|
||||
|
||||
Assurez-vous qu'il contient les données souhaitées et que les valeurs soient dans un format JSON valides (si vous utilisez une `JSONResponse`).
|
||||
|
||||
!!! note "Détails techniques"
|
||||
Vous pouvez également utiliser `from starlette.responses import JSONResponse`.
|
||||
|
||||
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec l'objet `statut`.
|
||||
|
||||
## Documents OpenAPI et API
|
||||
|
||||
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (la documentation de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
|
||||
|
||||
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -446,7 +446,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -67,6 +67,9 @@ nav:
|
||||
- tutorial/query-params.md
|
||||
- tutorial/body.md
|
||||
- tutorial/background-tasks.md
|
||||
- Guide utilisateur avancé:
|
||||
- advanced/additional-status-codes.md
|
||||
- advanced/additional-responses.md
|
||||
- async.md
|
||||
- Déploiement:
|
||||
- deployment/index.md
|
||||
|
||||
@@ -445,7 +445,7 @@ item: Item
|
||||
|
||||
בשימוש Starlette:
|
||||
|
||||
- <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - דרוש אם ברצונכם להשתמש ב - `TestClient`.
|
||||
- <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - דרוש אם ברצונכם להשתמש ב - `TestClient`.
|
||||
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - דרוש אם ברצונכם להשתמש בברירת המחדל של תצורת הטמפלייטים.
|
||||
- <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - דרוש אם ברצונכם לתמוך ב <abbr title="המרת המחרוזת שמגיעה מבקשת HTTP למידע פייתון">"פרסור"</abbr> טפסים, באצמעות <code dir="ltr">request.form()</code>.
|
||||
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - דרוש אם ברצונכם להשתמש ב - `SessionMiddleware`.
|
||||
|
||||
@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -446,7 +446,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -423,7 +423,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -443,7 +443,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
186
docs/ja/docs/advanced/websockets.md
Normal file
186
docs/ja/docs/advanced/websockets.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# WebSocket
|
||||
|
||||
**FastAPI**で<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSocket</a>が使用できます。
|
||||
|
||||
## `WebSockets`のインストール
|
||||
|
||||
まず `WebSockets`のインストールが必要です。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install websockets
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## WebSocket クライアント
|
||||
|
||||
### 本番環境
|
||||
|
||||
本番環境では、React、Vue.js、Angularなどの最新のフレームワークで作成されたフロントエンドを使用しているでしょう。
|
||||
|
||||
そして、バックエンドとWebSocketを使用して通信するために、おそらくフロントエンドのユーティリティを使用することになるでしょう。
|
||||
|
||||
または、ネイティブコードでWebSocketバックエンドと直接通信するネイティブモバイルアプリケーションがあるかもしれません。
|
||||
|
||||
他にも、WebSocketのエンドポイントと通信する方法があるかもしれません。
|
||||
|
||||
---
|
||||
|
||||
ただし、この例では非常にシンプルなHTML文書といくつかのJavaScriptを、すべてソースコードの中に入れて使用することにします。
|
||||
|
||||
もちろん、これは最適な方法ではありませんし、本番環境で使うことはないでしょう。
|
||||
|
||||
本番環境では、上記の方法のいずれかの選択肢を採用することになるでしょう。
|
||||
|
||||
しかし、これはWebSocketのサーバーサイドに焦点を当て、実用的な例を示す最も簡単な方法です。
|
||||
|
||||
```Python hl_lines="2 6-38 41-43"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
## `websocket` を作成する
|
||||
|
||||
**FastAPI** アプリケーションで、`websocket` を作成します。
|
||||
|
||||
```Python hl_lines="1 46-47"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "技術詳細"
|
||||
`from starlette.websockets import WebSocket` を使用しても構いません.
|
||||
|
||||
**FastAPI** は開発者の利便性のために、同じ `WebSocket` を提供します。しかし、こちらはStarletteから直接提供されるものです。
|
||||
|
||||
## メッセージの送受信
|
||||
|
||||
WebSocketルートでは、 `await` を使ってメッセージの送受信ができます。
|
||||
|
||||
```Python hl_lines="48-52"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
バイナリやテキストデータ、JSONデータを送受信できます。
|
||||
|
||||
## 試してみる
|
||||
|
||||
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
|
||||
|
||||
次のようなシンプルなページが表示されます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image01.png">
|
||||
|
||||
入力ボックスにメッセージを入力して送信できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image02.png">
|
||||
|
||||
そして、 WebSocketを使用した**FastAPI**アプリケーションが応答します。
|
||||
|
||||
<img src="/img/tutorial/websockets/image03.png">
|
||||
|
||||
複数のメッセージを送信(および受信)できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image04.png">
|
||||
|
||||
そして、これらの通信はすべて同じWebSocket接続を使用します。
|
||||
|
||||
## 依存関係
|
||||
|
||||
WebSocketエンドポイントでは、`fastapi` から以下をインポートして使用できます。
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
これらは、他のFastAPI エンドポイント/*path operation* の場合と同じように機能します。
|
||||
|
||||
```Python hl_lines="58-65 68-83"
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info "情報"
|
||||
WebSocket で `HTTPException` を発生させることはあまり意味がありません。したがって、WebSocketの接続を直接閉じる方がよいでしょう。
|
||||
|
||||
クロージングコードは、<a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">仕様で定義された有効なコード</a>の中から使用することができます。
|
||||
|
||||
将来的には、どこからでも `raise` できる `WebSocketException` が用意され、専用の例外ハンドラを追加できるようになる予定です。これは、Starlette の <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> に依存するものです。
|
||||
|
||||
### 依存関係を用いてWebSocketsを試してみる
|
||||
|
||||
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --reload
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
|
||||
|
||||
クライアントが設定できる項目は以下の通りです。
|
||||
|
||||
* パスで使用される「Item ID」
|
||||
* クエリパラメータとして使用される「Token」
|
||||
|
||||
!!! tip "豆知識"
|
||||
クエリ `token` は依存パッケージによって処理されることに注意してください。
|
||||
|
||||
これにより、WebSocketに接続してメッセージを送受信できます。
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## 切断や複数クライアントへの対応
|
||||
|
||||
WebSocket接続が閉じられると、 `await websocket.receive_text()` は例外 `WebSocketDisconnect` を発生させ、この例のようにキャッチして処理することができます。
|
||||
|
||||
```Python hl_lines="81-83"
|
||||
{!../../../docs_src/websockets/tutorial003.py!}
|
||||
```
|
||||
|
||||
試してみるには、
|
||||
|
||||
* いくつかのブラウザタブでアプリを開きます。
|
||||
* それらのタブでメッセージを記入してください。
|
||||
* そして、タブのうち1つを閉じてください。
|
||||
|
||||
これにより例外 `WebSocketDisconnect` が発生し、他のすべてのクライアントは次のようなメッセージを受信します。
|
||||
|
||||
```
|
||||
Client #1596980209979 left the chat
|
||||
```
|
||||
|
||||
!!! tip "豆知識"
|
||||
上記のアプリは、複数の WebSocket 接続に対してメッセージを処理し、ブロードキャストする方法を示すための最小限のシンプルな例です。
|
||||
|
||||
しかし、すべての接続がメモリ内の単一のリストで処理されるため、プロセスの実行中にのみ機能し、単一のプロセスでのみ機能することに注意してください。
|
||||
|
||||
もしFastAPIと簡単に統合できて、RedisやPostgreSQLなどでサポートされている、より堅牢なものが必要なら、<a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a> を確認してください。
|
||||
|
||||
## その他のドキュメント
|
||||
|
||||
オプションの詳細については、Starletteのドキュメントを確認してください。
|
||||
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank"> `WebSocket` クラス</a>
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">クラスベースのWebSocket処理</a>
|
||||
@@ -169,7 +169,7 @@ FastAPIには非常に使いやすく、非常に強力な<abbr title='also know
|
||||
* **GraphQL**のサポート
|
||||
* プロセス内バックグラウンドタスク
|
||||
* 起動およびシャットダウンイベント
|
||||
* `requests`に基づいて構築されたテストクライアント
|
||||
* `httpx`に基づいて構築されたテストクライアント
|
||||
* **CORS**、GZip、静的ファイル、ストリーミング応答
|
||||
* **セッションとCookie**のサポート
|
||||
* テストカバレッジ100%
|
||||
|
||||
@@ -416,7 +416,7 @@ item: Item
|
||||
- 以下のようなたくさんのおまけ機能(Starlette のおかげです):
|
||||
- **WebSockets**
|
||||
- **GraphQL**
|
||||
- `requests` や `pytest`をもとにした極限に簡単なテスト
|
||||
- `httpx` や `pytest`をもとにした極限に簡単なテスト
|
||||
- **CORS**
|
||||
- **クッキーセッション**
|
||||
- ...などなど。
|
||||
@@ -436,7 +436,7 @@ Pydantic によって使用されるもの:
|
||||
|
||||
Starlette によって使用されるもの:
|
||||
|
||||
- <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - `TestClient`を使用するために必要です。
|
||||
- <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - `TestClient`を使用するために必要です。
|
||||
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - デフォルトのテンプレート設定を使用する場合は必要です。
|
||||
- <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>`request.form()`からの変換をサポートしたい場合は必要です。
|
||||
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` サポートのためには必要です。
|
||||
|
||||
@@ -64,7 +64,7 @@ http://127.0.0.1:8000/items/?skip=20
|
||||
|
||||
同様に、デフォルト値を `None` とすることで、オプショナルなクエリパラメータを宣言できます:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -82,7 +82,7 @@ http://127.0.0.1:8000/items/?skip=20
|
||||
|
||||
`bool` 型も宣言できます。これは以下の様に変換されます:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -126,7 +126,7 @@ http://127.0.0.1:8000/items/foo?short=yes
|
||||
|
||||
名前で判別されます:
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
```Python hl_lines="8 10"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -184,7 +184,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
|
||||
そして当然、あるパラメータを必須に、別のパラメータにデフォルト値を設定し、また別のパラメータをオプショナルにできます:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a> のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
|
||||
|
||||
<a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a> がベースなので、非常に使いやすく直感的です。
|
||||
<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> がベースなので、非常に使いやすく直感的です。
|
||||
|
||||
これを使用すると、**FastAPI** と共に <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> を直接利用できます。
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
`test_` から始まる名前の関数を作成します (これは `pytest` の標準的なコンベンションです)。
|
||||
|
||||
`requests` と同じ様に `TestClient` オブジェクトを使用します。
|
||||
`httpx` と同じ様に `TestClient` オブジェクトを使用します。
|
||||
|
||||
チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
|
||||
```
|
||||
|
||||
リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`requests` での実現方法を検索 (Google) できます。
|
||||
リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法を検索 (Google) できます。
|
||||
|
||||
テストでも同じことを行います。
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
* *ヘッダー* を渡すには、`headers` パラメータに `dict` を渡します。
|
||||
* *cookies* の場合、 `cookies` パラメータに `dict` です。
|
||||
|
||||
(`requests` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、<a href="http://docs.python-requests.org" class="external-link" target="_blank">Requestsのドキュメント</a>を確認してください。
|
||||
(`httpx` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPXのドキュメント</a>を確認してください。
|
||||
|
||||
!!! info "情報"
|
||||
`TestClient` は、Pydanticモデルではなく、JSONに変換できるデータを受け取ることに注意してください。
|
||||
|
||||
@@ -86,6 +86,7 @@ nav:
|
||||
- advanced/response-directly.md
|
||||
- advanced/custom-response.md
|
||||
- advanced/nosql-databases.md
|
||||
- advanced/websockets.md
|
||||
- advanced/conditional-openapi.md
|
||||
- async.md
|
||||
- デプロイ:
|
||||
|
||||
@@ -422,7 +422,7 @@ item: Item
|
||||
* (Starlette 덕분에) 많은 추가 기능:
|
||||
* **웹 소켓**
|
||||
* **GraphQL**
|
||||
* `requests` 및 `pytest`에 기반한 극히 쉬운 테스트
|
||||
* HTTPX 및 `pytest`에 기반한 극히 쉬운 테스트
|
||||
* **CORS**
|
||||
* **쿠키 세션**
|
||||
* ...기타 등등.
|
||||
@@ -442,7 +442,7 @@ Pydantic이 사용하는:
|
||||
|
||||
Starlette이 사용하는:
|
||||
|
||||
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - `TestClient`를 사용하려면 필요.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - `TestClient`를 사용하려면 필요.
|
||||
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr>의 지원을 원하면 필요.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` 지원을 위해 필요.
|
||||
|
||||
@@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -449,7 +449,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -420,7 +420,7 @@ Dla bardziej kompletnych przykładów posiadających więcej funkcjonalności, z
|
||||
* Wiele dodatkowych funkcji (dzięki Starlette) takie jak:
|
||||
* **WebSockety**
|
||||
* **GraphQL**
|
||||
* bardzo proste testy bazujące na `requests` oraz `pytest`
|
||||
* bardzo proste testy bazujące na HTTPX oraz `pytest`
|
||||
* **CORS**
|
||||
* **Sesje cookie**
|
||||
* ...i więcej.
|
||||
@@ -440,7 +440,7 @@ Używane przez Pydantic:
|
||||
|
||||
Używane przez Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Wymagane jeżeli chcesz korzystać z `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Wymagane jeżeli chcesz korzystać z `TestClient`.
|
||||
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Wymagane jeżeli chcesz korzystać z `FileResponse` albo `StaticFiles`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Wymagane jeżeli chcesz używać domyślnej konfiguracji szablonów.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Wymagane jeżelich chcesz wsparcie <abbr title="przetwarzania stringa którzy przychodzi z żądaniem HTTP na dane używane przez Pythona">"parsowania"</abbr> formularzy, używając `request.form()`.
|
||||
|
||||
@@ -167,7 +167,7 @@ Com **FastAPI**, você terá todos os recursos do **Starlette** (já que FastAPI
|
||||
* Suporte a **GraphQL**.
|
||||
* Tarefas em processo _background_.
|
||||
* Eventos na inicialização e encerramento.
|
||||
* Cliente de testes construído sobre `requests`.
|
||||
* Cliente de testes construído sobre HTTPX.
|
||||
* Respostas em **CORS**, GZip, Static Files, Streaming.
|
||||
* Suporte a **Session e Cookie**.
|
||||
* 100% de cobertura de testes.
|
||||
|
||||
@@ -415,7 +415,7 @@ Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fas
|
||||
* Muitos recursos extras (graças ao Starlette) como:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* testes extrememamente fáceis baseados em `requests` e `pytest`
|
||||
* testes extrememamente fáceis baseados em HTTPX e `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...e mais.
|
||||
@@ -435,7 +435,7 @@ Usados por Pydantic:
|
||||
|
||||
Usados por Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Necessário se você quiser utilizar o `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Necessário se você quiser utilizar o `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Necessário se você quiser utilizar a configuração padrão de templates.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Necessário se você quiser suporte com <abbr title="converte uma string que chega de uma requisição HTTP para dados Python">"parsing"</abbr> de formulário, com `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Necessário para suporte a `SessionMiddleware`.
|
||||
|
||||
36
docs/pt/docs/tutorial/request-forms-and-files.md
Normal file
36
docs/pt/docs/tutorial/request-forms-and-files.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Formulários e Arquivos da Requisição
|
||||
|
||||
Você pode definir arquivos e campos de formulário ao mesmo tempo usando `File` e `Form`.
|
||||
|
||||
!!! info "Informação"
|
||||
Para receber arquivos carregados e/ou dados de formulário, primeiro instale <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>.
|
||||
|
||||
Por exemplo: `pip install python-multipart`.
|
||||
|
||||
|
||||
## Importe `File` e `Form`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Defina parâmetros de `File` e `Form`
|
||||
|
||||
Crie parâmetros de arquivo e formulário da mesma forma que você faria para `Body` ou `Query`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
Os arquivos e campos de formulário serão carregados como dados de formulário e você receberá os arquivos e campos de formulário.
|
||||
|
||||
E você pode declarar alguns dos arquivos como `bytes` e alguns como `UploadFile`.
|
||||
|
||||
!!! warning "Aviso"
|
||||
Você pode declarar vários parâmetros `File` e `Form` em uma *operação de caminho*, mas não é possível declarar campos `Body` para receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
|
||||
|
||||
Isso não é uma limitação do **FastAPI** , é parte do protocolo HTTP.
|
||||
|
||||
## Recapitulando
|
||||
|
||||
Usar `File` e `Form` juntos quando precisar receber dados e arquivos na mesma requisição.
|
||||
@@ -75,6 +75,7 @@ nav:
|
||||
- tutorial/header-params.md
|
||||
- tutorial/response-status-code.md
|
||||
- tutorial/request-forms.md
|
||||
- tutorial/request-forms-and-files.md
|
||||
- tutorial/handling-errors.md
|
||||
- Segurança:
|
||||
- tutorial/security/index.md
|
||||
|
||||
@@ -169,7 +169,7 @@ FastAPI включает в себя чрезвычайно простую в и
|
||||
* Поддержка **WebSocket**.
|
||||
* Фоновые задачи для процессов.
|
||||
* События запуска и выключения.
|
||||
* Тестовый клиент построен на библиотеке `requests`.
|
||||
* Тестовый клиент построен на библиотеке HTTPX.
|
||||
* **CORS**, GZip, статические файлы, потоковые ответы.
|
||||
* Поддержка **сессий и cookie**.
|
||||
* 100% покрытие тестами.
|
||||
|
||||
@@ -424,7 +424,7 @@ item: Item
|
||||
* **GraphQL** интеграция с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
|
||||
* Множество дополнительных функций (благодаря Starlette), таких как:
|
||||
* **Веб-сокеты**
|
||||
* очень простые тесты на основе `requests` и `pytest`
|
||||
* очень простые тесты на основе HTTPX и `pytest`
|
||||
* **CORS**
|
||||
* **Cookie сеансы(сессии)**
|
||||
* ...и многое другое.
|
||||
@@ -444,7 +444,7 @@ item: Item
|
||||
|
||||
Используется Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Обязательно, если вы хотите использовать `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Обязательно, если вы хотите использовать `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Обязательно, если вы хотите использовать конфигурацию шаблона по умолчанию.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать форму <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинга"</abbr> с помощью `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Обязательно, для поддержки `SessionMiddleware`.
|
||||
|
||||
@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -446,7 +446,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -449,7 +449,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -174,7 +174,7 @@ Bütün entegrasyonlar kullanımı kolay olmak üzere (zorunluluklar ile beraber
|
||||
* **GraphQL** desteği.
|
||||
* Kullanım halinde arka plan işlevleri.
|
||||
* Başlatma ve kapatma eventleri(startup and shutdown).
|
||||
* Test sunucusu `requests` üzerine kurulu.
|
||||
* Test sunucusu HTTPX üzerine kurulu.
|
||||
* **CORS**, GZip, Static dosyalar, Streaming responseları.
|
||||
* **Session and Cookie** desteği.
|
||||
* 100% test kapsayıcılığı.
|
||||
|
||||
@@ -434,7 +434,7 @@ Daha fazla örnek ve özellik için <a href="https://fastapi.tiangolo.com/tutori
|
||||
* Diğer ekstra özellikler (Starlette sayesinde):
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* `requests` ve `pytest` sayesinde aşırı kolay testler.
|
||||
* HTTPX ve `pytest` sayesinde aşırı kolay testler.
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...ve daha fazlası.
|
||||
@@ -454,7 +454,7 @@ Pydantic tarafında kullanılan:
|
||||
|
||||
Starlette tarafında kullanılan:
|
||||
|
||||
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Eğer `TestClient` kullanmak istiyorsan gerekli.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Eğer `TestClient` kullanmak istiyorsan gerekli.
|
||||
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Eğer kendine ait template konfigürasyonu oluşturmak istiyorsan gerekli
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Form kullanmak istiyorsan gerekli <abbr title="HTTP bağlantısından gelen stringi Python objesine çevirmek için">("dönüşümü")</abbr>.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` desteği için gerekli.
|
||||
|
||||
@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
* Many extra features (thanks to Starlette) as:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* extremely easy tests based on `requests` and `pytest`
|
||||
* extremely easy tests based on HTTPX and `pytest`
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ...and more.
|
||||
@@ -446,7 +446,7 @@ Used by Pydantic:
|
||||
|
||||
Used by Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
|
||||
|
||||
@@ -171,7 +171,7 @@ FastAPI 有一个使用非常简单,但是非常强大的<abbr title='也叫
|
||||
* **支持 GraphQL** 。
|
||||
* 后台任务处理。
|
||||
* Startup 和 shutdown 事件。
|
||||
* 测试客户端基于 `requests`。
|
||||
* 测试客户端基于 HTTPX。
|
||||
* **CORS**, GZip, 静态文件, 流响应。
|
||||
* 支持 **Session 和 Cookie** 。
|
||||
* 100% 测试覆盖率。
|
||||
|
||||
@@ -422,7 +422,7 @@ item: Item
|
||||
* 许多额外功能(归功于 Starlette)比如:
|
||||
* **WebSockets**
|
||||
* **GraphQL**
|
||||
* 基于 `requests` 和 `pytest` 的极其简单的测试
|
||||
* 基于 HTTPX 和 `pytest` 的极其简单的测试
|
||||
* **CORS**
|
||||
* **Cookie Sessions**
|
||||
* ......以及更多
|
||||
@@ -442,7 +442,7 @@ item: Item
|
||||
|
||||
用于 Starlette:
|
||||
|
||||
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - 使用 `TestClient` 时安装。
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - 使用 `TestClient` 时安装。
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 使用默认模板配置时安装。
|
||||
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - 需要通过 `request.form()` 对表单进行<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">「解析」</abbr>时安装。
|
||||
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - 需要 `SessionMiddleware` 支持时安装。
|
||||
|
||||
@@ -107,7 +107,7 @@ async def get_current_user(
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
authenticate_value = "Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
|
||||
@@ -106,7 +106,7 @@ async def get_current_user(
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
authenticate_value = "Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
|
||||
@@ -107,7 +107,7 @@ async def get_current_user(
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
authenticate_value = "Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI, Query, WebSocket, status
|
||||
from fastapi import (
|
||||
Cookie,
|
||||
Depends,
|
||||
FastAPI,
|
||||
Query,
|
||||
WebSocket,
|
||||
WebSocketException,
|
||||
status,
|
||||
)
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
@@ -61,7 +69,7 @@ async def get_cookie_or_token(
|
||||
token: Union[str, None] = Query(default=None),
|
||||
):
|
||||
if session is None and token is None:
|
||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
|
||||
return session or token
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.86.0"
|
||||
__version__ = "0.87.0"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
@@ -8,6 +8,7 @@ from .applications import FastAPI as FastAPI
|
||||
from .background import BackgroundTasks as BackgroundTasks
|
||||
from .datastructures import UploadFile as UploadFile
|
||||
from .exceptions import HTTPException as HTTPException
|
||||
from .exceptions import WebSocketException as WebSocketException
|
||||
from .param_functions import Body as Body
|
||||
from .param_functions import Cookie as Cookie
|
||||
from .param_functions import Depends as Depends
|
||||
|
||||
@@ -426,21 +426,21 @@ def is_coroutine_callable(call: Callable[..., Any]) -> bool:
|
||||
return inspect.iscoroutinefunction(call)
|
||||
if inspect.isclass(call):
|
||||
return False
|
||||
dunder_call = getattr(call, "__call__", None)
|
||||
dunder_call = getattr(call, "__call__", None) # noqa: B004
|
||||
return inspect.iscoroutinefunction(dunder_call)
|
||||
|
||||
|
||||
def is_async_gen_callable(call: Callable[..., Any]) -> bool:
|
||||
if inspect.isasyncgenfunction(call):
|
||||
return True
|
||||
dunder_call = getattr(call, "__call__", None)
|
||||
dunder_call = getattr(call, "__call__", None) # noqa: B004
|
||||
return inspect.isasyncgenfunction(dunder_call)
|
||||
|
||||
|
||||
def is_gen_callable(call: Callable[..., Any]) -> bool:
|
||||
if inspect.isgeneratorfunction(call):
|
||||
return True
|
||||
dunder_call = getattr(call, "__call__", None)
|
||||
dunder_call = getattr(call, "__call__", None) # noqa: B004
|
||||
return inspect.isgeneratorfunction(dunder_call)
|
||||
|
||||
|
||||
@@ -724,14 +724,14 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
||||
# in case a sub-dependency is evaluated with a single unique body field
|
||||
# That is combined (embedded) with other body fields
|
||||
for param in flat_dependant.body_params:
|
||||
setattr(param.field_info, "embed", True)
|
||||
setattr(param.field_info, "embed", True) # noqa: B010
|
||||
model_name = "Body_" + name
|
||||
BodyModel: Type[BaseModel] = create_model(model_name)
|
||||
for f in flat_dependant.body_params:
|
||||
BodyModel.__fields__[f.name] = f
|
||||
required = any(True for f in flat_dependant.body_params if f.required)
|
||||
|
||||
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
|
||||
BodyFieldInfo_kwargs: Dict[str, Any] = {"default": None}
|
||||
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
|
||||
BodyFieldInfo: Type[params.Body] = params.File
|
||||
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
|
||||
@@ -740,7 +740,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
||||
BodyFieldInfo = params.Body
|
||||
|
||||
body_param_media_types = [
|
||||
getattr(f.field_info, "media_type")
|
||||
f.field_info.media_type
|
||||
for f in flat_dependant.body_params
|
||||
if isinstance(f.field_info, params.Body)
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Any, Dict, Optional, Sequence, Type
|
||||
from pydantic import BaseModel, ValidationError, create_model
|
||||
from pydantic.error_wrappers import ErrorList
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.exceptions import WebSocketException as WebSocketException # noqa: F401
|
||||
|
||||
|
||||
class HTTPException(StarletteHTTPException):
|
||||
|
||||
@@ -701,7 +701,7 @@ class APIRouter(routing.Router):
|
||||
), "A path prefix must not end with '/', as the routes will start with '/'"
|
||||
else:
|
||||
for r in router.routes:
|
||||
path = getattr(r, "path")
|
||||
path = getattr(r, "path") # noqa: B009
|
||||
name = getattr(r, "name", "unknown")
|
||||
if path is not None and not path:
|
||||
raise Exception(
|
||||
|
||||
@@ -54,7 +54,7 @@ class APIKeyHeader(APIKeyBase):
|
||||
self.auto_error = auto_error
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
api_key: str = request.headers.get(self.model.name)
|
||||
api_key = request.headers.get(self.model.name)
|
||||
if not api_key:
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -38,7 +38,7 @@ class HTTPBase(SecurityBase):
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
if self.auto_error:
|
||||
@@ -67,7 +67,7 @@ class HTTPBasic(HTTPBase):
|
||||
async def __call__( # type: ignore
|
||||
self, request: Request
|
||||
) -> Optional[HTTPBasicCredentials]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if self.realm:
|
||||
unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'}
|
||||
@@ -113,7 +113,7 @@ class HTTPBearer(HTTPBase):
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
if self.auto_error:
|
||||
@@ -148,7 +148,7 @@ class HTTPDigest(HTTPBase):
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
if self.auto_error:
|
||||
|
||||
@@ -126,7 +126,7 @@ class OAuth2(SecurityBase):
|
||||
self.auto_error = auto_error
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
if not authorization:
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
@@ -157,7 +157,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
if self.auto_error:
|
||||
@@ -200,7 +200,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
if self.auto_error:
|
||||
|
||||
@@ -23,7 +23,7 @@ class OpenIdConnect(SecurityBase):
|
||||
self.auto_error = auto_error
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.headers.get("Authorization")
|
||||
authorization = request.headers.get("Authorization")
|
||||
if not authorization:
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import Tuple
|
||||
from typing import Optional, Tuple
|
||||
|
||||
|
||||
def get_authorization_scheme_param(authorization_header_value: str) -> Tuple[str, str]:
|
||||
def get_authorization_scheme_param(
|
||||
authorization_header_value: Optional[str],
|
||||
) -> Tuple[str, str]:
|
||||
if not authorization_header_value:
|
||||
return "", ""
|
||||
scheme, _, param = authorization_header_value.partition(" ")
|
||||
|
||||
@@ -39,7 +39,7 @@ classifiers = [
|
||||
"Topic :: Internet :: WWW/HTTP",
|
||||
]
|
||||
dependencies = [
|
||||
"starlette==0.20.4",
|
||||
"starlette==0.21.0",
|
||||
"pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
@@ -53,10 +53,9 @@ test = [
|
||||
"pytest >=7.1.3,<8.0.0",
|
||||
"coverage[toml] >= 6.5.0,<7.0",
|
||||
"mypy ==0.982",
|
||||
"flake8 >=3.8.3,<6.0.0",
|
||||
"ruff ==0.0.114",
|
||||
"black == 22.8.0",
|
||||
"isort >=5.0.6,<6.0.0",
|
||||
"requests >=2.24.0,<3.0.0",
|
||||
"httpx >=0.23.0,<0.24.0",
|
||||
"email_validator >=1.1.1,<2.0.0",
|
||||
# TODO: once removing databases from tutorial, upgrade SQLAlchemy
|
||||
@@ -88,21 +87,20 @@ doc = [
|
||||
"pyyaml >=5.3.1,<7.0.0",
|
||||
]
|
||||
dev = [
|
||||
"autoflake >=1.4.0,<2.0.0",
|
||||
"flake8 >=3.8.3,<6.0.0",
|
||||
"ruff ==0.0.114",
|
||||
"uvicorn[standard] >=0.12.0,<0.19.0",
|
||||
"pre-commit >=2.17.0,<3.0.0",
|
||||
]
|
||||
all = [
|
||||
"requests >=2.24.0,<3.0.0",
|
||||
"jinja2 >=2.11.2,<4.0.0",
|
||||
"python-multipart >=0.0.5,<0.0.6",
|
||||
"itsdangerous >=1.1.0,<3.0.0",
|
||||
"pyyaml >=5.3.1,<7.0.0",
|
||||
"ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0",
|
||||
"orjson >=3.2.1,<4.0.0",
|
||||
"email_validator >=1.1.1,<2.0.0",
|
||||
"uvicorn[standard] >=0.12.0,<0.19.0",
|
||||
"httpx >=0.23.0",
|
||||
"jinja2 >=2.11.2",
|
||||
"python-multipart >=0.0.5",
|
||||
"itsdangerous >=1.1.0",
|
||||
"pyyaml >=5.3.1",
|
||||
"ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0",
|
||||
"orjson >=3.2.1",
|
||||
"email_validator >=1.1.1",
|
||||
"uvicorn[standard] >=0.12.0",
|
||||
]
|
||||
|
||||
[tool.hatch.version]
|
||||
@@ -157,3 +155,30 @@ source = [
|
||||
"fastapi"
|
||||
]
|
||||
context = '${CONTEXT}'
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"F", # pyflakes
|
||||
# "I", # isort
|
||||
"C", # flake8-comprehensions
|
||||
"B", # flake8-bugbear
|
||||
]
|
||||
ignore = [
|
||||
"E501", # line too long, handled by black
|
||||
"B008", # do not perform function calls in argument defaults
|
||||
]
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = ["F401"]
|
||||
"docs_src/dependencies/tutorial007.py" = ["F821"]
|
||||
"docs_src/dependencies/tutorial008.py" = ["F821"]
|
||||
"docs_src/dependencies/tutorial009.py" = ["F821"]
|
||||
"docs_src/dependencies/tutorial010.py" = ["F821"]
|
||||
"docs_src/custom_response/tutorial007.py" = ["B007"]
|
||||
"docs_src/dataclasses/tutorial003.py" = ["I001"]
|
||||
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-third-party = ["fastapi", "pydantic", "starlette"]
|
||||
|
||||
@@ -332,7 +332,7 @@ def serve():
|
||||
os.chdir("site")
|
||||
server_address = ("", 8008)
|
||||
server = HTTPServer(server_address, SimpleHTTPRequestHandler)
|
||||
typer.echo(f"Serving at: http://127.0.0.1:8008")
|
||||
typer.echo("Serving at: http://127.0.0.1:8008")
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ def get_file_to_nav_map(nav: list) -> Dict[str, Tuple[str, ...]]:
|
||||
file_to_nav = {}
|
||||
for item in nav:
|
||||
if type(item) is str:
|
||||
file_to_nav[item] = tuple()
|
||||
file_to_nav[item] = ()
|
||||
elif type(item) is dict:
|
||||
item_key = list(item.keys())[0]
|
||||
sub_nav = item[item_key]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh -e
|
||||
set -x
|
||||
|
||||
autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place docs_src fastapi tests scripts --exclude=__init__.py
|
||||
ruff fastapi tests docs_src scripts --fix
|
||||
black fastapi tests docs_src scripts
|
||||
isort fastapi tests docs_src scripts
|
||||
|
||||
@@ -4,6 +4,6 @@ set -e
|
||||
set -x
|
||||
|
||||
mypy fastapi
|
||||
flake8 fastapi tests
|
||||
ruff fastapi tests docs_src scripts
|
||||
black fastapi tests --check
|
||||
isort fastapi tests docs_src scripts --check-only
|
||||
|
||||
@@ -110,6 +110,6 @@ def test_route_classes():
|
||||
for r in app.router.routes:
|
||||
assert isinstance(r, Route)
|
||||
routes[r.path] = r
|
||||
assert getattr(routes["/a/"], "x_type") == "A"
|
||||
assert getattr(routes["/a/b/"], "x_type") == "B"
|
||||
assert getattr(routes["/a/b/c/"], "x_type") == "C"
|
||||
assert getattr(routes["/a/"], "x_type") == "A" # noqa: B009
|
||||
assert getattr(routes["/a/b/"], "x_type") == "B" # noqa: B009
|
||||
assert getattr(routes["/a/b/c/"], "x_type") == "C" # noqa: B009
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_schema():
|
||||
|
||||
|
||||
def test_get_invalid():
|
||||
response = client.get("/foo", params={"client_id": None})
|
||||
response = client.get("/foo")
|
||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ def test_get_api_route_not_decorated():
|
||||
|
||||
|
||||
def test_delete():
|
||||
response = client.delete("/items/foo", json={"name": "Foo"})
|
||||
response = client.request("DELETE", "/items/foo", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"item_id": "foo", "item": {"name": "Foo", "price": None}}
|
||||
|
||||
|
||||
@@ -104,5 +104,5 @@ def test_openapi_schema():
|
||||
|
||||
def test_get_with_body():
|
||||
body = {"name": "Foo", "description": "Some description", "price": 5.5}
|
||||
response = client.get("/product", json=body)
|
||||
response = client.request("GET", "/product", json=body)
|
||||
assert response.json() == body
|
||||
|
||||
@@ -33,8 +33,6 @@ async def hidden_query(
|
||||
return {"hidden_query": hidden_query}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_shema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
@@ -161,6 +159,7 @@ openapi_shema = {
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
client = TestClient(app)
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == openapi_shema
|
||||
@@ -184,7 +183,8 @@ def test_openapi_schema():
|
||||
],
|
||||
)
|
||||
def test_hidden_cookie(path, cookies, expected_status, expected_response):
|
||||
response = client.get(path, cookies=cookies)
|
||||
client = TestClient(app, cookies=cookies)
|
||||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
@@ -207,12 +207,14 @@ def test_hidden_cookie(path, cookies, expected_status, expected_response):
|
||||
],
|
||||
)
|
||||
def test_hidden_header(path, headers, expected_status, expected_response):
|
||||
client = TestClient(app)
|
||||
response = client.get(path, headers=headers)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
|
||||
def test_hidden_path():
|
||||
client = TestClient(app)
|
||||
response = client.get("/hidden_path/hidden_path")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"hidden_path": "hidden_path"}
|
||||
@@ -234,6 +236,7 @@ def test_hidden_path():
|
||||
],
|
||||
)
|
||||
def test_hidden_query(path, expected_status, expected_response):
|
||||
client = TestClient(app)
|
||||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
@@ -22,8 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
@@ -51,18 +49,21 @@ openapi_schema = {
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
client = TestClient(app)
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_security_api_key():
|
||||
response = client.get("/users/me", cookies={"key": "secret"})
|
||||
client = TestClient(app, cookies={"key": "secret"})
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "secret"}
|
||||
|
||||
|
||||
def test_security_api_key_no_key():
|
||||
client = TestClient(app)
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 403, response.text
|
||||
assert response.json() == {"detail": "Not authenticated"}
|
||||
|
||||
@@ -22,8 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
@@ -56,18 +54,21 @@ openapi_schema = {
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
client = TestClient(app)
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_security_api_key():
|
||||
response = client.get("/users/me", cookies={"key": "secret"})
|
||||
client = TestClient(app, cookies={"key": "secret"})
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "secret"}
|
||||
|
||||
|
||||
def test_security_api_key_no_key():
|
||||
client = TestClient(app)
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 403, response.text
|
||||
assert response.json() == {"detail": "Not authenticated"}
|
||||
|
||||
@@ -29,8 +29,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
@@ -58,18 +56,21 @@ openapi_schema = {
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
client = TestClient(app)
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_security_api_key():
|
||||
response = client.get("/users/me", cookies={"key": "secret"})
|
||||
client = TestClient(app, cookies={"key": "secret"})
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "secret"}
|
||||
|
||||
|
||||
def test_security_api_key_no_key():
|
||||
client = TestClient(app)
|
||||
response = client.get("/users/me")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"msg": "Create an account first"}
|
||||
|
||||
@@ -4,7 +4,6 @@ from typing import Optional
|
||||
from fastapi import FastAPI, Security
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.testclient import TestClient
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -51,8 +50,7 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_security_http_basic():
|
||||
auth = HTTPBasicAuth(username="john", password="secret")
|
||||
response = client.get("/users/me", auth=auth)
|
||||
response = client.get("/users/me", auth=("john", "secret"))
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "john", "password": "secret"}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from base64 import b64encode
|
||||
from fastapi import FastAPI, Security
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.testclient import TestClient
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -48,8 +47,7 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_security_http_basic():
|
||||
auth = HTTPBasicAuth(username="john", password="secret")
|
||||
response = client.get("/users/me", auth=auth)
|
||||
response = client.get("/users/me", auth=("john", "secret"))
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "john", "password": "secret"}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from base64 import b64encode
|
||||
from fastapi import FastAPI, Security
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.testclient import TestClient
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -54,8 +53,7 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_security_http_basic():
|
||||
auth = HTTPBasicAuth(username="john", password="secret")
|
||||
response = client.get("/users/me", auth=auth)
|
||||
response = client.get("/users/me", auth=("john", "secret"))
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "john", "password": "secret"}
|
||||
|
||||
|
||||
@@ -252,16 +252,14 @@ def test_tuple_with_model_invalid():
|
||||
|
||||
|
||||
def test_tuple_form_valid():
|
||||
response = client.post("/tuple-form/", data=[("values", "1"), ("values", "2")])
|
||||
response = client.post("/tuple-form/", data={"values": ("1", "2")})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [1, 2]
|
||||
|
||||
|
||||
def test_tuple_form_invalid():
|
||||
response = client.post(
|
||||
"/tuple-form/", data=[("values", "1"), ("values", "2"), ("values", "3")]
|
||||
)
|
||||
response = client.post("/tuple-form/", data={"values": ("1", "2", "3")})
|
||||
assert response.status_code == 422, response.text
|
||||
|
||||
response = client.post("/tuple-form/", data=[("values", "1")])
|
||||
response = client.post("/tuple-form/", data={"values": ("1")})
|
||||
assert response.status_code == 422, response.text
|
||||
|
||||
@@ -9,6 +9,6 @@ def test_middleware():
|
||||
assert response.status_code == 200, response.text
|
||||
|
||||
client = TestClient(app)
|
||||
response = client.get("/", allow_redirects=False)
|
||||
response = client.get("/", follow_redirects=False)
|
||||
assert response.status_code == 307, response.text
|
||||
assert response.headers["location"] == "https://testserver/"
|
||||
|
||||
@@ -176,7 +176,7 @@ def test_post_broken_body():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"content-type": "application/json"},
|
||||
data="{some broken json}",
|
||||
content="{some broken json}",
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
@@ -214,7 +214,7 @@ def test_post_form_for_json():
|
||||
def test_explicit_content_type():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
@@ -223,7 +223,7 @@ def test_explicit_content_type():
|
||||
def test_geo_json():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
headers={"Content-Type": "application/geo+json"},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
@@ -232,7 +232,7 @@ def test_geo_json():
|
||||
def test_no_content_type_is_json():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
@@ -255,17 +255,19 @@ def test_wrong_headers():
|
||||
]
|
||||
}
|
||||
|
||||
response = client.post("/items/", data=data, headers={"Content-Type": "text/plain"})
|
||||
response = client.post(
|
||||
"/items/", content=data, headers={"Content-Type": "text/plain"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
|
||||
response = client.post(
|
||||
"/items/", data=data, headers={"Content-Type": "application/geo+json-seq"}
|
||||
"/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
response = client.post(
|
||||
"/items/", data=data, headers={"Content-Type": "application/not-really-json"}
|
||||
"/items/", content=data, headers={"Content-Type": "application/not-really-json"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
|
||||
@@ -185,7 +185,7 @@ def test_post_broken_body(client: TestClient):
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"content-type": "application/json"},
|
||||
data="{some broken json}",
|
||||
content="{some broken json}",
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
@@ -225,7 +225,7 @@ def test_post_form_for_json(client: TestClient):
|
||||
def test_explicit_content_type(client: TestClient):
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
@@ -235,7 +235,7 @@ def test_explicit_content_type(client: TestClient):
|
||||
def test_geo_json(client: TestClient):
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
headers={"Content-Type": "application/geo+json"},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
@@ -245,7 +245,7 @@ def test_geo_json(client: TestClient):
|
||||
def test_no_content_type_is_json(client: TestClient):
|
||||
response = client.post(
|
||||
"/items/",
|
||||
data='{"name": "Foo", "price": 50.5}',
|
||||
content='{"name": "Foo", "price": 50.5}',
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
@@ -269,17 +269,19 @@ def test_wrong_headers(client: TestClient):
|
||||
]
|
||||
}
|
||||
|
||||
response = client.post("/items/", data=data, headers={"Content-Type": "text/plain"})
|
||||
response = client.post(
|
||||
"/items/", content=data, headers={"Content-Type": "text/plain"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
|
||||
response = client.post(
|
||||
"/items/", data=data, headers={"Content-Type": "application/geo+json-seq"}
|
||||
"/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
response = client.post(
|
||||
"/items/", data=data, headers={"Content-Type": "application/not-really-json"}
|
||||
"/items/", content=data, headers={"Content-Type": "application/not-really-json"}
|
||||
)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == invalid_dict
|
||||
|
||||
@@ -3,8 +3,6 @@ from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.cookie_params.tutorial001 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
@@ -88,6 +86,7 @@ openapi_schema = {
|
||||
],
|
||||
)
|
||||
def test(path, cookies, expected_status, expected_response):
|
||||
response = client.get(path, cookies=cookies)
|
||||
client = TestClient(app, cookies=cookies)
|
||||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
@@ -70,14 +70,6 @@ openapi_schema = {
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.cookie_params.tutorial001_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
@pytest.mark.parametrize(
|
||||
"path,cookies,expected_status,expected_response",
|
||||
@@ -94,7 +86,10 @@ def get_client():
|
||||
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
|
||||
],
|
||||
)
|
||||
def test(path, cookies, expected_status, expected_response, client: TestClient):
|
||||
response = client.get(path, cookies=cookies)
|
||||
def test(path, cookies, expected_status, expected_response):
|
||||
from docs_src.cookie_params.tutorial001_py310 import app
|
||||
|
||||
client = TestClient(app, cookies=cookies)
|
||||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
@@ -26,7 +26,7 @@ def test_gzip_request(compress):
|
||||
data = gzip.compress(data)
|
||||
headers["Content-Encoding"] = "gzip"
|
||||
headers["Content-Type"] = "application/json"
|
||||
response = client.post("/sum", data=data, headers=headers)
|
||||
response = client.post("/sum", content=data, headers=headers)
|
||||
assert response.json() == {"sum": n}
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,6 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_get():
|
||||
response = client.get("/typer", allow_redirects=False)
|
||||
response = client.get("/typer", follow_redirects=False)
|
||||
assert response.status_code == 307, response.text
|
||||
assert response.headers["location"] == "https://typer.tiangolo.com"
|
||||
|
||||
@@ -27,6 +27,6 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_redirect_response_class():
|
||||
response = client.get("/fastapi", allow_redirects=False)
|
||||
response = client.get("/fastapi", follow_redirects=False)
|
||||
assert response.status_code == 307
|
||||
assert response.headers["location"] == "https://fastapi.tiangolo.com"
|
||||
|
||||
@@ -27,6 +27,6 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_redirect_status_code():
|
||||
response = client.get("/pydantic", allow_redirects=False)
|
||||
response = client.get("/pydantic", follow_redirects=False)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "https://pydantic-docs.helpmanual.io/"
|
||||
|
||||
@@ -47,7 +47,7 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_post():
|
||||
response = client.post("/items/", data=b"this is actually not validated")
|
||||
response = client.post("/items/", content=b"this is actually not validated")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"size": 30,
|
||||
|
||||
@@ -58,7 +58,7 @@ def test_post():
|
||||
- x-men
|
||||
- x-avengers
|
||||
"""
|
||||
response = client.post("/items/", data=yaml_data)
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"name": "Deadpoolio",
|
||||
@@ -74,7 +74,7 @@ def test_post_broken_yaml():
|
||||
x - x-men
|
||||
x - x-avengers
|
||||
"""
|
||||
response = client.post("/items/", data=yaml_data)
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {"detail": "Invalid YAML"}
|
||||
|
||||
@@ -88,7 +88,7 @@ def test_post_invalid():
|
||||
- x-avengers
|
||||
- sneaky: object
|
||||
"""
|
||||
response = client.post("/items/", data=yaml_data)
|
||||
response = client.post("/items/", content=yaml_data)
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from base64 import b64encode
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
from docs_src.security.tutorial006 import app
|
||||
|
||||
@@ -38,8 +37,7 @@ def test_openapi_schema():
|
||||
|
||||
|
||||
def test_security_http_basic():
|
||||
auth = HTTPBasicAuth(username="john", password="secret")
|
||||
response = client.get("/users/me", auth=auth)
|
||||
response = client.get("/users/me", auth=("john", "secret"))
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"username": "john", "password": "secret"}
|
||||
|
||||
|
||||
@@ -4,20 +4,18 @@ from fastapi.websockets import WebSocketDisconnect
|
||||
|
||||
from docs_src.websockets.tutorial002 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_main():
|
||||
client = TestClient(app)
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert b"<!DOCTYPE html>" in response.content
|
||||
|
||||
|
||||
def test_websocket_with_cookie():
|
||||
client = TestClient(app, cookies={"session": "fakesession"})
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
with client.websocket_connect(
|
||||
"/items/foo/ws", cookies={"session": "fakesession"}
|
||||
) as websocket:
|
||||
with client.websocket_connect("/items/foo/ws") as websocket:
|
||||
message = "Message one"
|
||||
websocket.send_text(message)
|
||||
data = websocket.receive_text()
|
||||
@@ -33,6 +31,7 @@ def test_websocket_with_cookie():
|
||||
|
||||
|
||||
def test_websocket_with_header():
|
||||
client = TestClient(app)
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
with client.websocket_connect("/items/bar/ws?token=some-token") as websocket:
|
||||
message = "Message one"
|
||||
@@ -50,6 +49,7 @@ def test_websocket_with_header():
|
||||
|
||||
|
||||
def test_websocket_with_header_and_query():
|
||||
client = TestClient(app)
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
with client.websocket_connect("/items/2/ws?q=3&token=some-token") as websocket:
|
||||
message = "Message one"
|
||||
@@ -71,6 +71,7 @@ def test_websocket_with_header_and_query():
|
||||
|
||||
|
||||
def test_websocket_no_credentials():
|
||||
client = TestClient(app)
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
with client.websocket_connect("/items/foo/ws"):
|
||||
pytest.fail(
|
||||
@@ -79,6 +80,7 @@ def test_websocket_no_credentials():
|
||||
|
||||
|
||||
def test_websocket_invalid_data():
|
||||
client = TestClient(app)
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
with client.websocket_connect("/items/foo/ws?q=bar&token=some-token"):
|
||||
pytest.fail(
|
||||
|
||||
@@ -111,7 +111,7 @@ def test_router_ws_depends():
|
||||
|
||||
def test_router_ws_depends_with_override():
|
||||
client = TestClient(app)
|
||||
app.dependency_overrides[ws_dependency] = lambda: "Override"
|
||||
app.dependency_overrides[ws_dependency] = lambda: "Override" # noqa: E731
|
||||
with client.websocket_connect("/router-ws-depends/") as websocket:
|
||||
assert websocket.receive_text() == "Override"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user