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