mirror of
https://github.com/rjNemo/fastapi
synced 2026-06-11 13:06:43 +00:00
✨ Add mermaid.js support in Markdown fenced blocks for diagrams (#985)
This commit is contained in:
parent
26c345b766
commit
25e94d6344
5 changed files with 133 additions and 1 deletions
|
|
@ -102,6 +102,74 @@ You can have any combinations of dependencies that you want.
|
||||||
|
|
||||||
**FastAPI** uses them internally to achieve this.
|
**FastAPI** uses them internally to achieve this.
|
||||||
|
|
||||||
|
## Dependencies with `yield` and `HTTPException`
|
||||||
|
|
||||||
|
You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions.
|
||||||
|
|
||||||
|
It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**.
|
||||||
|
|
||||||
|
The exit code in dependencies with `yield` is executed *after* [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`).
|
||||||
|
|
||||||
|
So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore.
|
||||||
|
|
||||||
|
This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.
|
||||||
|
|
||||||
|
Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*.
|
||||||
|
|
||||||
|
But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system.
|
||||||
|
|
||||||
|
If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code.
|
||||||
|
|
||||||
|
If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
|
||||||
|
|
||||||
|
The sequence of execution is more or less like this:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
|
||||||
|
participant client as Client
|
||||||
|
participant handler as Exception handler
|
||||||
|
participant dep as Dep with yield
|
||||||
|
participant operation as Path Operation
|
||||||
|
participant tasks as Background tasks
|
||||||
|
|
||||||
|
Note over client,tasks: Can raise exception for dependency, handled after response is sent
|
||||||
|
Note over client,operation: Can raise HTTPException and can change the response
|
||||||
|
client ->> dep: Start request
|
||||||
|
Note over dep: Code up to yield
|
||||||
|
opt raise
|
||||||
|
dep -->> handler: Raise HTTPException
|
||||||
|
handler -->> client: HTTP error response
|
||||||
|
dep -->> dep: Raise other exception
|
||||||
|
end
|
||||||
|
dep ->> operation: Run dependency, e.g. DB session
|
||||||
|
opt raise
|
||||||
|
operation -->> handler: Raise HTTPException
|
||||||
|
handler -->> client: HTTP error response
|
||||||
|
operation -->> dep: Raise other exception
|
||||||
|
end
|
||||||
|
operation ->> client: Return response to client
|
||||||
|
Note over client,operation: Response is already sent, can't change it anymore
|
||||||
|
opt Tasks
|
||||||
|
operation -->> tasks: Send background tasks
|
||||||
|
end
|
||||||
|
opt Raise other exception
|
||||||
|
tasks -->> dep: Raise other exception
|
||||||
|
end
|
||||||
|
Note over dep: After yield
|
||||||
|
opt Handle other exception
|
||||||
|
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
This diagram shows `HTTPException`, but you could also raise any other exception that you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} for. And that exception would be handled by that custom exception handler instead of the dependency exit code.
|
||||||
|
|
||||||
|
But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.
|
||||||
|
|
||||||
## Context Managers
|
## Context Managers
|
||||||
|
|
||||||
### What are "Context Managers"
|
### What are "Context Managers"
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,19 @@ Whenever a new request arrives, **FastAPI** will take care of:
|
||||||
* Get the result from your function.
|
* Get the result from your function.
|
||||||
* Assign that result to the parameter in your *path operation function*.
|
* Assign that result to the parameter in your *path operation function*.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
|
||||||
|
common_parameters(["common_parameters"])
|
||||||
|
read_items["/items/"]
|
||||||
|
read_users["/users/"]
|
||||||
|
|
||||||
|
common_parameters --> read_items
|
||||||
|
common_parameters --> read_users
|
||||||
|
```
|
||||||
|
|
||||||
|
This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
|
||||||
|
|
||||||
!!! check
|
!!! check
|
||||||
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
|
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
|
||||||
|
|
||||||
|
|
@ -154,7 +167,39 @@ Although the hierarchical dependency injection system is very simple to define a
|
||||||
|
|
||||||
You can define dependencies that in turn can define dependencies themselves.
|
You can define dependencies that in turn can define dependencies themselves.
|
||||||
|
|
||||||
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and your dependencies) and providing (injecting) the results at each step.
|
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
|
||||||
|
|
||||||
|
For example, let's say you have 4 API endpoints (*path operations*):
|
||||||
|
|
||||||
|
* `/items/public/`
|
||||||
|
* `/items/private/`
|
||||||
|
* `/users/{user_id}/activate`
|
||||||
|
* `/items/pro/`
|
||||||
|
|
||||||
|
then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
|
||||||
|
current_user(["current_user"])
|
||||||
|
active_user(["active_user"])
|
||||||
|
admin_user(["admin_user"])
|
||||||
|
paying_user(["paying_user"])
|
||||||
|
|
||||||
|
public["/items/public/"]
|
||||||
|
private["/items/private/"]
|
||||||
|
activate_user["/users/{user_id}/activate"]
|
||||||
|
pro_items["/items/pro/"]
|
||||||
|
|
||||||
|
current_user --> active_user
|
||||||
|
active_user --> admin_user
|
||||||
|
active_user --> paying_user
|
||||||
|
|
||||||
|
current_user --> public
|
||||||
|
active_user --> private
|
||||||
|
admin_user --> activate_user
|
||||||
|
paying_user --> pro_items
|
||||||
|
```
|
||||||
|
|
||||||
## Integrated with **OpenAPI**
|
## Integrated with **OpenAPI**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,17 @@ Then we can use the dependency with:
|
||||||
|
|
||||||
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
|
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
|
||||||
|
query_extractor(["query_extractor"])
|
||||||
|
query_or_cookie_extractor(["query_or_cookie_extractor"])
|
||||||
|
|
||||||
|
read_query["/items/"]
|
||||||
|
|
||||||
|
query_extractor --> query_or_cookie_extractor --> read_query
|
||||||
|
```
|
||||||
|
|
||||||
## Using the same dependency multiple times
|
## Using the same dependency multiple times
|
||||||
|
|
||||||
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
|
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
|
||||||
|
|
|
||||||
|
|
@ -469,6 +469,8 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in
|
||||||
|
|
||||||
This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
|
This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
|
||||||
|
|
||||||
|
But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank}
|
||||||
|
|
||||||
And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
|
And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
|
||||||
|
|
||||||
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
|
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,11 @@ markdown_extensions:
|
||||||
- admonition
|
- admonition
|
||||||
- codehilite
|
- codehilite
|
||||||
- extra
|
- extra
|
||||||
|
- pymdownx.superfences:
|
||||||
|
custom_fences:
|
||||||
|
- name: mermaid
|
||||||
|
class: mermaid
|
||||||
|
format: !!python/name:pymdownx.superfences.fence_div_format
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
social:
|
social:
|
||||||
|
|
@ -137,4 +142,5 @@ extra_css:
|
||||||
- 'css/custom.css'
|
- 'css/custom.css'
|
||||||
|
|
||||||
extra_javascript:
|
extra_javascript:
|
||||||
|
- 'https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js'
|
||||||
- 'js/custom.js'
|
- 'js/custom.js'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue