mirror of https://github.com/tiangolo/fastapi.git
✨ Allow hiding from OpenAPI (and Swagger UI) `Query`, `Cookie`, `Header`, and `Path` parameters (#3144)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
parent
347e391271
commit
ca5d57ea79
|
|
@ -387,6 +387,22 @@ The docs will show it like this:
|
|||
|
||||
<img src="/img/tutorial/query-params-str-validations/image01.png">
|
||||
|
||||
## Exclude from OpenAPI
|
||||
|
||||
To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter `include_in_schema` of `Query` to `False`:
|
||||
|
||||
=== "Python 3.6 and above"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.10 and above"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
You can declare additional validations and metadata for your parameters.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(
|
||||
hidden_query: Optional[str] = Query(None, include_in_schema=False)
|
||||
):
|
||||
if hidden_query:
|
||||
return {"hidden_query": hidden_query}
|
||||
else:
|
||||
return {"hidden_query": "Not found"}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(hidden_query: str | None = Query(None, include_in_schema=False)):
|
||||
if hidden_query:
|
||||
return {"hidden_query": hidden_query}
|
||||
else:
|
||||
return {"hidden_query": "Not found"}
|
||||
|
|
@ -92,6 +92,8 @@ def get_openapi_operation_parameters(
|
|||
for param in all_route_params:
|
||||
field_info = param.field_info
|
||||
field_info = cast(Param, field_info)
|
||||
if not field_info.include_in_schema:
|
||||
continue
|
||||
parameter = {
|
||||
"name": param.alias,
|
||||
"in": field_info.in_.value,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ def Path( # noqa: N802
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
) -> Any:
|
||||
return params.Path(
|
||||
|
|
@ -37,6 +38,7 @@ def Path( # noqa: N802
|
|||
example=example,
|
||||
examples=examples,
|
||||
deprecated=deprecated,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -57,6 +59,7 @@ def Query( # noqa: N802
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
) -> Any:
|
||||
return params.Query(
|
||||
|
|
@ -74,6 +77,7 @@ def Query( # noqa: N802
|
|||
example=example,
|
||||
examples=examples,
|
||||
deprecated=deprecated,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -95,6 +99,7 @@ def Header( # noqa: N802
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
) -> Any:
|
||||
return params.Header(
|
||||
|
|
@ -113,6 +118,7 @@ def Header( # noqa: N802
|
|||
example=example,
|
||||
examples=examples,
|
||||
deprecated=deprecated,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -133,6 +139,7 @@ def Cookie( # noqa: N802
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
) -> Any:
|
||||
return params.Cookie(
|
||||
|
|
@ -150,6 +157,7 @@ def Cookie( # noqa: N802
|
|||
example=example,
|
||||
examples=examples,
|
||||
deprecated=deprecated,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,13 @@ class Param(FieldInfo):
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
):
|
||||
self.deprecated = deprecated
|
||||
self.example = example
|
||||
self.examples = examples
|
||||
self.include_in_schema = include_in_schema
|
||||
super().__init__(
|
||||
default,
|
||||
alias=alias,
|
||||
|
|
@ -75,6 +77,7 @@ class Path(Param):
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
):
|
||||
self.in_ = self.in_
|
||||
|
|
@ -93,6 +96,7 @@ class Path(Param):
|
|||
deprecated=deprecated,
|
||||
example=example,
|
||||
examples=examples,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -117,6 +121,7 @@ class Query(Param):
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
|
|
@ -134,6 +139,7 @@ class Query(Param):
|
|||
deprecated=deprecated,
|
||||
example=example,
|
||||
examples=examples,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -159,6 +165,7 @@ class Header(Param):
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
):
|
||||
self.convert_underscores = convert_underscores
|
||||
|
|
@ -177,6 +184,7 @@ class Header(Param):
|
|||
deprecated=deprecated,
|
||||
example=example,
|
||||
examples=examples,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
@ -201,6 +209,7 @@ class Cookie(Param):
|
|||
example: Any = Undefined,
|
||||
examples: Optional[Dict[str, Any]] = None,
|
||||
deprecated: Optional[bool] = None,
|
||||
include_in_schema: bool = True,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
|
|
@ -218,6 +227,7 @@ class Cookie(Param):
|
|||
deprecated=deprecated,
|
||||
example=example,
|
||||
examples=examples,
|
||||
include_in_schema=include_in_schema,
|
||||
**extra,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,239 @@
|
|||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from fastapi import Cookie, FastAPI, Header, Path, Query
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/hidden_cookie")
|
||||
async def hidden_cookie(
|
||||
hidden_cookie: Optional[str] = Cookie(None, include_in_schema=False)
|
||||
):
|
||||
return {"hidden_cookie": hidden_cookie}
|
||||
|
||||
|
||||
@app.get("/hidden_header")
|
||||
async def hidden_header(
|
||||
hidden_header: Optional[str] = Header(None, include_in_schema=False)
|
||||
):
|
||||
return {"hidden_header": hidden_header}
|
||||
|
||||
|
||||
@app.get("/hidden_path/{hidden_path}")
|
||||
async def hidden_path(hidden_path: str = Path(..., include_in_schema=False)):
|
||||
return {"hidden_path": hidden_path}
|
||||
|
||||
|
||||
@app.get("/hidden_query")
|
||||
async def hidden_query(
|
||||
hidden_query: Optional[str] = Query(None, include_in_schema=False)
|
||||
):
|
||||
return {"hidden_query": hidden_query}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_shema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/hidden_cookie": {
|
||||
"get": {
|
||||
"summary": "Hidden Cookie",
|
||||
"operationId": "hidden_cookie_hidden_cookie_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/hidden_header": {
|
||||
"get": {
|
||||
"summary": "Hidden Header",
|
||||
"operationId": "hidden_header_hidden_header_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/hidden_path/{hidden_path}": {
|
||||
"get": {
|
||||
"summary": "Hidden Path",
|
||||
"operationId": "hidden_path_hidden_path__hidden_path__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"/hidden_query": {
|
||||
"get": {
|
||||
"summary": "Hidden Query",
|
||||
"operationId": "hidden_query_hidden_query_get",
|
||||
"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"},
|
||||
}
|
||||
},
|
||||
},
|
||||
"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
|
||||
assert response.json() == openapi_shema
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,cookies,expected_status,expected_response",
|
||||
[
|
||||
(
|
||||
"/hidden_cookie",
|
||||
{},
|
||||
200,
|
||||
{"hidden_cookie": None},
|
||||
),
|
||||
(
|
||||
"/hidden_cookie",
|
||||
{"hidden_cookie": "somevalue"},
|
||||
200,
|
||||
{"hidden_cookie": "somevalue"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hidden_cookie(path, cookies, expected_status, expected_response):
|
||||
response = client.get(path, cookies=cookies)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,headers,expected_status,expected_response",
|
||||
[
|
||||
(
|
||||
"/hidden_header",
|
||||
{},
|
||||
200,
|
||||
{"hidden_header": None},
|
||||
),
|
||||
(
|
||||
"/hidden_header",
|
||||
{"Hidden-Header": "somevalue"},
|
||||
200,
|
||||
{"hidden_header": "somevalue"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hidden_header(path, headers, expected_status, expected_response):
|
||||
response = client.get(path, headers=headers)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
|
||||
def test_hidden_path():
|
||||
response = client.get("/hidden_path/hidden_path")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"hidden_path": "hidden_path"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,expected_status,expected_response",
|
||||
[
|
||||
(
|
||||
"/hidden_query",
|
||||
200,
|
||||
{"hidden_query": None},
|
||||
),
|
||||
(
|
||||
"/hidden_query?hidden_query=somevalue",
|
||||
200,
|
||||
{"hidden_query": "somevalue"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hidden_query(path, expected_status, expected_response):
|
||||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.query_params_str_validations.tutorial014 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"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"},
|
||||
}
|
||||
},
|
||||
},
|
||||
"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_hidden_query():
|
||||
response = client.get("/items?hidden_query=somevalue")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"hidden_query": "somevalue"}
|
||||
|
||||
|
||||
def test_no_hidden_query():
|
||||
response = client.get("/items")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"hidden_query": "Not found"}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py310
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"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"},
|
||||
}
|
||||
},
|
||||
},
|
||||
"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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client():
|
||||
from docs_src.query_params_str_validations.tutorial014_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_openapi_schema(client: TestClient):
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_hidden_query(client: TestClient):
|
||||
response = client.get("/items?hidden_query=somevalue")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"hidden_query": "somevalue"}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_no_hidden_query(client: TestClient):
|
||||
response = client.get("/items")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"hidden_query": "Not found"}
|
||||
Loading…
Reference in New Issue