6 changed files with 273 additions and 39 deletions
@ -0,0 +1,118 @@ |
|||
--- a/fastapi/routing.py
|
|||
+++ b/fastapi/routing.py
|
|||
@@ -2581,6 +2581,89 @@ class APIRouter:
|
|||
generate_unique_id_function=generate_unique_id_function, |
|||
) |
|||
|
|||
+ def query(
|
|||
+ self,
|
|||
+ path: Annotated[
|
|||
+ str,
|
|||
+ Doc(
|
|||
+ """
|
|||
+ The URL path to be used for this *path operation*.
|
|||
+
|
|||
+ For example, in `http://example.com/items`, the path is `/items`.
|
|||
+ """
|
|||
+ ),
|
|||
+ ],
|
|||
+ *,
|
|||
+ response_model: Annotated[
|
|||
+ Any,
|
|||
+ Doc(
|
|||
+ """
|
|||
+ The type to use for the response.
|
|||
+
|
|||
+ It could be any valid Pydantic *field* type. So, it doesn't have to
|
|||
+ be a Pydantic model, it could be other things, like a `list`, `dict`,
|
|||
+ etc.
|
|||
+
|
|||
+ It will be used for:
|
|||
+
|
|||
+ * Documentation: the generated OpenAPI (and the UI at `/docs`) will
|
|||
+ show it as the response (JSON Schema).
|
|||
+ * Serialization: you could return an arbitrary object and the
|
|||
+ `response_model` would be used to serialize that object into the
|
|||
+ corresponding JSON.
|
|||
+ * Filtering: the JSON sent to the client will only contain the data
|
|||
+ (fields) defined in the `response_model`. If you returned an object
|
|||
+ that contains an attribute `password` but the `response_model` does
|
|||
+ not include that field, the JSON sent to the client would not have
|
|||
+ that `password`.
|
|||
+ * Validation: whatever you return will be serialized with the
|
|||
+ `response_model`, converting any data as necessary to generate the
|
|||
+ corresponding JSON. But if the data in the object returned is not
|
|||
+ valid, that would mean a violation of the contract with the client,
|
|||
+ so it's an error from the API developer. So, FastAPI will raise an
|
|||
+ error and return a 500 error code (Internal Server Error).
|
|||
+
|
|||
+ Read more about it in the
|
|||
+ [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
|
|||
+ """
|
|||
+ ),
|
|||
+ ] = Default(None),
|
|||
+ status_code: Annotated[Optional[int], Doc("HTTP status code for the response")] = None,
|
|||
+ tags: Annotated[Optional[List[Union[str, Enum]]], Doc("OpenAPI tags")] = None,
|
|||
+ dependencies: Annotated[Optional[Sequence[params.Depends]], Doc("Dependencies")] = None,
|
|||
+ summary: Annotated[Optional[str], Doc("OpenAPI summary")] = None,
|
|||
+ description: Annotated[Optional[str], Doc("OpenAPI description")] = None,
|
|||
+ response_description: Annotated[str, Doc("OpenAPI response description")] = "Successful Response",
|
|||
+ responses: Annotated[Optional[Dict[Union[int, str], Dict[str, Any]]], Doc("Additional responses")] = None,
|
|||
+ deprecated: Annotated[Optional[bool], Doc("Mark as deprecated")] = None,
|
|||
+ operation_id: Annotated[Optional[str], Doc("OpenAPI operation ID")] = None,
|
|||
+ response_model_include: Annotated[Optional[IncEx], Doc("Fields to include")] = None,
|
|||
+ response_model_exclude: Annotated[Optional[IncEx], Doc("Fields to exclude")] = None,
|
|||
+ response_model_by_alias: Annotated[bool, Doc("Use field aliases")] = True,
|
|||
+ response_model_exclude_unset: Annotated[bool, Doc("Exclude unset fields")] = False,
|
|||
+ response_model_exclude_defaults: Annotated[bool, Doc("Exclude default values")] = False,
|
|||
+ response_model_exclude_none: Annotated[bool, Doc("Exclude None values")] = False,
|
|||
+ include_in_schema: Annotated[bool, Doc("Include in OpenAPI schema")] = True,
|
|||
+ response_class: Annotated[
|
|||
+ Union[Type[Response], DefaultPlaceholder],
|
|||
+ Doc("Response class to use"),
|
|||
+ ] = Default(JSONResponse),
|
|||
+ name: Annotated[Optional[str], Doc("Name for the operation")] = None,
|
|||
+ callbacks: Annotated[Optional[List[BaseRoute]], Doc("OpenAPI callbacks")] = None,
|
|||
+ openapi_extra: Annotated[Optional[Dict[str, Any]], Doc("Extra OpenAPI data")] = None,
|
|||
+ generate_unique_id_function: Annotated[
|
|||
+ Callable[[APIRoute], str], Doc("Function to generate unique IDs")
|
|||
+ ] = Default(generate_unique_id),
|
|||
+ ) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
|||
+ """
|
|||
+ Add a *path operation* using an HTTP QUERY operation.
|
|||
+
|
|||
+ The QUERY method is a safe HTTP method that allows request bodies,
|
|||
+ useful for complex queries that exceed URL length limits.
|
|||
+
|
|||
+ See: https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html
|
|||
+ """
|
|||
+ return self.api_route(
|
|||
+ path=path,
|
|||
+ response_model=response_model,
|
|||
+ status_code=status_code,
|
|||
+ tags=tags,
|
|||
+ dependencies=dependencies,
|
|||
+ summary=summary,
|
|||
+ description=description,
|
|||
+ response_description=response_description,
|
|||
+ responses=responses,
|
|||
+ deprecated=deprecated,
|
|||
+ methods=["QUERY"],
|
|||
+ operation_id=operation_id,
|
|||
+ response_model_include=response_model_include,
|
|||
+ response_model_exclude=response_model_exclude,
|
|||
+ response_model_by_alias=response_model_by_alias,
|
|||
+ response_model_exclude_unset=response_model_exclude_unset,
|
|||
+ response_model_exclude_defaults=response_model_exclude_defaults,
|
|||
+ response_model_exclude_none=response_model_exclude_none,
|
|||
+ include_in_schema=include_in_schema,
|
|||
+ response_class=response_class,
|
|||
+ name=name,
|
|||
+ callbacks=callbacks,
|
|||
+ openapi_extra=openapi_extra,
|
|||
+ generate_unique_id_function=generate_unique_id_function,
|
|||
+ )
|
|||
+
|
|||
def delete( |
|||
self, |
|||
path: Annotated[ |
|||
@ -0,0 +1,74 @@ |
|||
# Minimal implementation for QUERY method support in FastAPI |
|||
|
|||
# This is the method to add to the FastAPI class in applications.py after the post method |
|||
|
|||
def query( |
|||
self, |
|||
path: str, |
|||
*, |
|||
response_model: Any = Default(None), |
|||
status_code: Optional[int] = None, |
|||
tags: Optional[List[Union[str, Enum]]] = None, |
|||
dependencies: Optional[Sequence[params.Depends]] = None, |
|||
summary: Optional[str] = None, |
|||
description: Optional[str] = None, |
|||
response_description: str = "Successful Response", |
|||
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, |
|||
deprecated: Optional[bool] = None, |
|||
operation_id: Optional[str] = None, |
|||
response_model_include: Optional[IncEx] = None, |
|||
response_model_exclude: Optional[IncEx] = None, |
|||
response_model_by_alias: bool = True, |
|||
response_model_exclude_unset: bool = False, |
|||
response_model_exclude_defaults: bool = False, |
|||
response_model_exclude_none: bool = False, |
|||
include_in_schema: bool = True, |
|||
response_class: Union[Type[Response], DefaultPlaceholder] = Default(JSONResponse), |
|||
name: Optional[str] = None, |
|||
callbacks: Optional[List[BaseRoute]] = None, |
|||
openapi_extra: Optional[Dict[str, Any]] = None, |
|||
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(generate_unique_id), |
|||
) -> Callable[[DecoratedCallable], DecoratedCallable]: |
|||
""" |
|||
Add a *path operation* using an HTTP QUERY operation. |
|||
|
|||
The QUERY method is a safe HTTP method that allows request bodies, |
|||
useful for complex queries that exceed URL length limits. |
|||
|
|||
## Example |
|||
|
|||
```python |
|||
from fastapi import FastAPI |
|||
|
|||
app = FastAPI() |
|||
|
|||
@app.query("/search/") |
|||
def search_items(query: SearchQuery): |
|||
return {"results": [...]} |
|||
``` |
|||
""" |
|||
return self.router.query( |
|||
path, |
|||
response_model=response_model, |
|||
status_code=status_code, |
|||
tags=tags, |
|||
dependencies=dependencies, |
|||
summary=summary, |
|||
description=description, |
|||
response_description=response_description, |
|||
responses=responses, |
|||
deprecated=deprecated, |
|||
operation_id=operation_id, |
|||
response_model_include=response_model_include, |
|||
response_model_exclude=response_model_exclude, |
|||
response_model_by_alias=response_model_by_alias, |
|||
response_model_exclude_unset=response_model_exclude_unset, |
|||
response_model_exclude_defaults=response_model_exclude_defaults, |
|||
response_model_exclude_none=response_model_exclude_none, |
|||
include_in_schema=include_in_schema, |
|||
response_class=response_class, |
|||
name=name, |
|||
callbacks=callbacks, |
|||
openapi_extra=openapi_extra, |
|||
generate_unique_id_function=generate_unique_id_function, |
|||
) |
|||
@ -0,0 +1,42 @@ |
|||
#!/usr/bin/env python3 |
|||
""" |
|||
Test script for the new QUERY method in FastAPI |
|||
""" |
|||
|
|||
from fastapi import FastAPI |
|||
from pydantic import BaseModel |
|||
from typing import List, Dict, Any |
|||
|
|||
app = FastAPI() |
|||
|
|||
class SearchQuery(BaseModel): |
|||
terms: List[str] |
|||
filters: Dict[str, Any] = {} |
|||
limit: int = 10 |
|||
|
|||
@app.query("/search/") |
|||
def search_items(query: SearchQuery): |
|||
""" |
|||
Search for items using the QUERY method. |
|||
|
|||
This demonstrates the new QUERY HTTP method that allows |
|||
complex request bodies for search operations. |
|||
""" |
|||
return { |
|||
"results": [ |
|||
{"id": 1, "name": "Item 1", "matches": query.terms}, |
|||
{"id": 2, "name": "Item 2", "matches": query.terms} |
|||
], |
|||
"query": query.dict(), |
|||
"total": 2 |
|||
} |
|||
|
|||
@app.get("/") |
|||
def read_root(): |
|||
return {"message": "FastAPI with QUERY method support"} |
|||
|
|||
if __name__ == "__main__": |
|||
import uvicorn |
|||
print("Testing FastAPI QUERY method implementation...") |
|||
print("Visit http://localhost:8000/docs to see the QUERY endpoint in action!") |
|||
uvicorn.run(app, host="0.0.0.0", port=8000) |
|||
Loading…
Reference in new issue