]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
✨ Add support for adding OpenAPI schema for GET requests with a body (#1626)
authorJAYATI SHRIVASTAVA <gaurijove@gmail.com>
Sun, 19 Jul 2020 10:17:50 +0000 (15:47 +0530)
committerGitHub <noreply@github.com>
Sun, 19 Jul 2020 10:17:50 +0000 (12:17 +0200)
* add test for get request body's openapi schema

* 📝 Update docs note for GET requests with body

* ✅ Update test for GET request with body, test it receives the body

* 🔇 Temporary type ignore while it's handled in Pydantic

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
docs/en/docs/tutorial/body.md
fastapi/dependencies/utils.py
fastapi/openapi/constants.py
tests/test_get_request_body.py [new file with mode: 0644]

index baa013809ef12db6ebaf355ac3a544cdb715b617..f5a75dfe055c0c35c2c10eaf96b46ee6518bd06a 100644 (file)
@@ -9,9 +9,11 @@ Your API almost always has to send a **response** body. But clients don't necess
 To declare a **request** body, you use <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> models with all their power and benefits.
 
 !!! info
-    You cannot send a request body using a `GET` operation (HTTP method).
+    To send data, you should use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`.
 
-    To send data, you have to use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`.
+    Sending a body with a `GET` request has an undefined behavior in the specifications, nevertheless, it is supported by FastAPI, only for very complex/extreme use cases.
+
+    As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using `GET`, and proxies in the middle might not support it.
 
 ## Import Pydantic's `BaseModel`
 
index 7fb7347c5fd09ce89c000b90f5d5c006131dc2ac..a09ad2512134bd3df8cc84ebda1379a07ceb0b54 100644 (file)
@@ -246,7 +246,9 @@ def get_typed_signature(call: Callable) -> inspect.Signature:
 def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any:
     annotation = param.annotation
     if isinstance(annotation, str):
-        annotation = ForwardRef(annotation)
+        # Temporary ignore type
+        # Ref: https://github.com/samuelcolvin/pydantic/issues/1738
+        annotation = ForwardRef(annotation)  # type: ignore
         annotation = evaluate_forwardref(annotation, globalns, globalns)
     return annotation
 
index bba050a1a2295c832925f5be3103bd42607a713a..000011a1fb4904885431015862997c0e861381d8 100644 (file)
@@ -1,3 +1,3 @@
-METHODS_WITH_BODY = set(("POST", "PUT", "DELETE", "PATCH"))
+METHODS_WITH_BODY = set(("GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"))
 STATUS_CODES_WITH_NO_BODY = set((100, 101, 102, 103, 204, 304))
 REF_PREFIX = "#/components/schemas/"
diff --git a/tests/test_get_request_body.py b/tests/test_get_request_body.py
new file mode 100644 (file)
index 0000000..348aee5
--- /dev/null
@@ -0,0 +1,108 @@
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class Product(BaseModel):
+    name: str
+    description: str = None
+    price: float
+
+
+@app.get("/product")
+async def create_item(product: Product):
+    return product
+
+
+client = TestClient(app)
+
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "FastAPI", "version": "0.1.0"},
+    "paths": {
+        "/product": {
+            "get": {
+                "summary": "Create Item",
+                "operationId": "create_item_product_get",
+                "requestBody": {
+                    "content": {
+                        "application/json": {
+                            "schema": {"$ref": "#/components/schemas/Product"}
+                        }
+                    },
+                    "required": True,
+                },
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "HTTPValidationError": {
+                "title": "HTTPValidationError",
+                "type": "object",
+                "properties": {
+                    "detail": {
+                        "title": "Detail",
+                        "type": "array",
+                        "items": {"$ref": "#/components/schemas/ValidationError"},
+                    }
+                },
+            },
+            "Product": {
+                "title": "Product",
+                "required": ["name", "price"],
+                "type": "object",
+                "properties": {
+                    "name": {"title": "Name", "type": "string"},
+                    "description": {"title": "Description", "type": "string"},
+                    "price": {"title": "Price", "type": "number"},
+                },
+            },
+            "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"},
+                },
+            },
+        }
+    },
+}
+
+
+def test_openapi_schema():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200, response.text
+    assert response.json() == openapi_schema
+
+
+def test_get_with_body():
+    body = {"name": "Foo", "description": "Some description", "price": 5.5}
+    response = client.get("/product", json=body)
+    assert response.json() == body